Skip to content

Commit

Permalink
expose drep active delegation; fix emulator; extract additional keys …
Browse files Browse the repository at this point in the history
…from certs
  • Loading branch information
alessandrokonrad committed Feb 5, 2025
1 parent e13206b commit 57bef49
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 38 deletions.
3 changes: 3 additions & 0 deletions src/core/libs/lucid_core/pkg/lucid_core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@ export interface Staking {
registered: boolean;
rewards: number;
poolId: string | undefined;
drep: DRep | undefined;
}

export type DRep = "Abstain" | "NoConfidence" | { Id: string };

export type Instructions = Instruction[];

export type Instruction = { type: "CollectFrom"; utxos: Utxo[]; redeemer?: string } | { type: "ReadFrom"; utxos: Utxo[] } | { type: "Mint"; assets: Assets; redeemer?: string } | { type: "PayTo"; assets: Assets; address: string; datumVariant?: DatumVariant; scriptRef?: Script } | { type: "PayToContract"; assets: Assets; address: string; datumVariant: DatumVariant; scriptRef?: Script } | { type: "DelegateTo"; delegation: Delegation; redeemer?: string } | { type: "RegisterStake"; rewardAddress: string } | { type: "DeregisterStake"; rewardAddress: string; redeemer?: string } | ({ type: "RegisterPool" } & PoolRegistration) | ({ type: "UpdatePool" } & PoolRegistration) | ({ type: "RetirePool" } & PoolRetirement) | { type: "Withdraw"; withdrawal: Withdrawal; redeemer?: string } | { type: "AddSigner"; keyHash: string } | { type: "AddNetworkId"; id: number } | { type: "ValidFrom"; unixTime: number } | { type: "ValidTo"; unixTime: number } | { type: "AttachMetadata"; metadata: [number, AuxMetadata] } | { type: "AttachMetadataWithConversion"; metadata: [number, AuxMetadata] } | { type: "AttachScript"; script: Script } | ({ type: "WithChangeTo" } & Change) | { type: "WithoutCoinSelection" };
Expand Down
Binary file modified src/core/libs/lucid_core/pkg/lucid_core_bg.wasm
Binary file not shown.
129 changes: 113 additions & 16 deletions src/core/libs/lucid_core/src/emulator_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,14 +449,7 @@ impl EmulatorState {
)));
}

withdrawals_requests.insert(
reward_address,
Staking {
registered: staking.registered,
rewards: *rewards,
pool_id: staking.pool_id.clone(),
},
);
withdrawals_requests.insert(reward_address, *rewards);
}
withdrawals_requests
} else {
Expand Down Expand Up @@ -626,6 +619,97 @@ impl EmulatorState {
variant: DelegVariant::Pool(pool_id),
}));
}
pallas_primitives::conway::Certificate::VoteDeleg(credential, drep) => {
let (reward_address, credential) = match credential {
pallas_primitives::conway::StakeCredential::AddrKeyhash(hash) => (
Addresses::credential_to_reward_address(
Network::Preprod,
Credential::Key {
hash: hash.to_string(),
},
)?,
Credential::Key {
hash: hash.to_string(),
},
),
pallas_primitives::conway::StakeCredential::ScriptHash(hash) => (
Addresses::credential_to_reward_address(
Network::Preprod,
Credential::Script {
hash: hash.to_string(),
},
)?,
Credential::Script {
hash: hash.to_string(),
},
),
};

let variant = match drep {
pallas_primitives::conway::DRep::Abstain => DelegVariant::Abstain,
pallas_primitives::conway::DRep::NoConfidence => {
DelegVariant::NoConfidence
}
pallas_primitives::conway::DRep::Key(key) => {
let mut key_with_header = Vec::new();
key_with_header.push(0b0010_0010);
key_with_header.extend(key.to_vec());
let drep = bech32::encode::<bech32::Bech32>(
Hrp::parse("drep").unwrap(),
&key_with_header,
)
.map_err(CoreError::msg)?;
DelegVariant::DRep(drep)
}
pallas_primitives::conway::DRep::Script(script) => {
let mut script_with_header = Vec::new();
script_with_header.push(0b0010_0011);
script_with_header.extend(script.to_vec());
let drep = bech32::encode::<bech32::Bech32>(
Hrp::parse("drep").unwrap(),
&script_with_header,
)
.map_err(CoreError::msg)?;
DelegVariant::DRep(drep)
}
};

check_and_consum(
credential,
Some(RedeemersKey {
tag: RedeemerTag::Cert,
index: index as u32,
}),
)?;

let staking = self
.staking
.get(&reward_address)
.cloned()
.unwrap_or_default();

if !staking.registered
&& !certificate_requests
.iter()
.find(|c| match c {
Certificate::Delegation(delegation) => {
&delegation.reward_address == &reward_address
}
_ => false,
})
.is_some()
{
return Err(CoreError::msg(format!(
"Stake registration not found: {}",
&reward_address
)));
}

certificate_requests.push(Certificate::Delegation(Delegation {
reward_address,
variant,
}));
}
_ => {}
}
}
Expand Down Expand Up @@ -727,9 +811,9 @@ impl EmulatorState {
}
}

for (reward_address, staking) in withdrawals_requests {
for (reward_address, rewards) in withdrawals_requests {
if let Some(entry) = self.staking.get_mut(&reward_address) {
entry.rewards -= staking.rewards;
entry.rewards -= rewards;
}
}

Expand All @@ -745,22 +829,25 @@ impl EmulatorState {
registered: true,
rewards: 0,
pool_id: None,
drep: None,
},
);
}
}
Certificate::StakeDeregistration { reward_address } => {
if let Some(staking) = self.staking.get_mut(&reward_address) {
staking.registered = false;
staking.pool_id = None;
}
self.staking.remove(&reward_address);
}
Certificate::Delegation(Delegation {
reward_address,
variant: DelegVariant::Pool(pool_id),
variant,
}) => {
if let Some(staking) = self.staking.get_mut(&reward_address) {
staking.pool_id = Some(pool_id);
match variant {
DelegVariant::Pool(pool_id) => staking.pool_id = Some(pool_id),
DelegVariant::Abstain => staking.drep = Some(DRep::Abstain),
DelegVariant::NoConfidence => staking.drep = Some(DRep::NoConfidence),
DelegVariant::DRep(id) => staking.drep = Some(DRep::Id(id)),
};
}
}
_ => {}
Expand Down Expand Up @@ -989,6 +1076,15 @@ pub struct Staking {
pub registered: bool,
pub rewards: u64,
pub pool_id: Option<String>,
pub drep: Option<DRep>,
}

#[derive(Tsify, Serialize, Deserialize, Debug, Clone)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub enum DRep {
Abstain,
NoConfidence,
Id(String),
}

impl Default for Staking {
Expand All @@ -997,6 +1093,7 @@ impl Default for Staking {
registered: false,
rewards: 0,
pool_id: None,
drep: None,
}
}
}
Expand Down
44 changes: 35 additions & 9 deletions src/core/libs/lucid_core/src/instruction_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,24 @@ impl InstructionSigner {
if let Some(certificates) = &tx.transaction_body.certificates {
for cert in certificates.iter() {
match cert {
pallas_primitives::conway::Certificate::StakeDeregistration(credential) => {
if let pallas_primitives::conway::StakeCredential::AddrKeyhash(hash) =
credential
{
used_keys.insert(hash.clone());
}
}
pallas_primitives::conway::Certificate::StakeDelegation(credential, _) => {
pallas_primitives::conway::Certificate::Reg(credential, _) // does this require a signature?
| pallas_primitives::conway::Certificate::UnReg(credential, _)
| pallas_primitives::conway::Certificate::VoteDeleg(credential, _)
| pallas_primitives::conway::Certificate::VoteRegDeleg(credential, _, _)
| pallas_primitives::conway::Certificate::StakeVoteDeleg(credential, _, _)
| pallas_primitives::conway::Certificate::StakeVoteRegDeleg(
credential,
_,
_,
_,
)
| pallas_primitives::conway::Certificate::StakeRegDeleg(credential, _, _)
| pallas_primitives::conway::Certificate::UnRegDRepCert(credential, _)
| pallas_primitives::conway::Certificate::RegDRepCert(credential, _, _)
| pallas_primitives::conway::Certificate::UpdateDRepCert(credential, _)
| pallas_primitives::conway::Certificate::StakeDeregistration(credential)
| pallas_primitives::conway::Certificate::ResignCommitteeCold(credential, _)
| pallas_primitives::conway::Certificate::StakeDelegation(credential, _) => {
if let pallas_primitives::conway::StakeCredential::AddrKeyhash(hash) =
credential
{
Expand All @@ -130,7 +140,23 @@ impl InstructionSigner {
pallas_primitives::conway::Certificate::PoolRetirement(key_hash, _) => {
used_keys.insert(key_hash.clone());
}
_ => (),
pallas_primitives::conway::Certificate::AuthCommitteeHot(
cold_credential,
hot_credential,
) => {
if let pallas_primitives::conway::StakeCredential::AddrKeyhash(hash) =
cold_credential
{
used_keys.insert(hash.clone());
}
if let pallas_primitives::conway::StakeCredential::AddrKeyhash(hash) =
hot_credential
// does this require a signature in the tx?
{
used_keys.insert(hash.clone());
}
}
pallas_primitives::conway::Certificate::StakeRegistration(_) => (),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {
Credential,
DatumVariant,
DRep,
Instruction,
InstructionSigner,
Network,
Expand Down Expand Up @@ -33,6 +34,7 @@ export type PartialInstruction =

export type ActiveDelegation = {
poolId: string | null;
drep: DRep | null;
rewards: bigint;
};

Expand Down
6 changes: 3 additions & 3 deletions src/lucid/lucid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ export class Lucid {

return rewardAddress
? await this.delegationAt(rewardAddress)
: { poolId: null, rewards: 0n };
: { poolId: null, drep: null, rewards: 0n };
},
sign: async (
instructionSigner: InstructionSigner,
Expand Down Expand Up @@ -374,7 +374,7 @@ export class Lucid {

return rewardAddress
? await this.delegationAt(rewardAddress)
: { poolId: null, rewards: 0n };
: { poolId: null, drep: null, rewards: 0n };
},
sign: () => {
throw new Error("Wallet is read only");
Expand Down Expand Up @@ -432,7 +432,7 @@ export class Lucid {
const rewardAddress = await this.wallet.rewardAddress();
return rewardAddress
? await this.delegationAt(rewardAddress)
: { poolId: null, rewards: 0n };
: { poolId: null, drep: null, rewards: 0n };
},
sign: (
instructionSigner: InstructionSigner,
Expand Down
12 changes: 11 additions & 1 deletion src/provider/blockfrost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,20 @@ export class Blockfrost implements Provider {
{ headers: { project_id: this.projectId, lucid } },
).then((res) => res.json());
if (!result || result.error) {
return { poolId: null, rewards: 0n };
return { poolId: null, drep: null, rewards: 0n };
}

const drep: ActiveDelegation["drep"] = result.drep_id
? result.drep_id.includes("abstain")
? "Abstain"
: result.drep_id.includes("confidence")
? "NoConfidence"
: { Id: result.drep_id }
: null;

return {
poolId: result.pool_id || null,
drep,
rewards: BigInt(result.withdrawable_amount),
};
}
Expand Down
1 change: 1 addition & 0 deletions src/provider/emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export class Emulator implements Provider {
const staking = this.state.getStaking(rewardAddress);
return Promise.resolve({
poolId: staking?.poolId || null,
drep: staking?.drep || null,
rewards: BigInt(staking?.rewards || 0),
});
}
Expand Down
8 changes: 6 additions & 2 deletions src/provider/kupmios.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
type ActiveDelegation,
Addresses,
type Assets,
type Credential,
fromUnit,
Expand Down Expand Up @@ -160,8 +161,10 @@ export class Kupmios implements Provider {

async getDelegation(rewardAddress: string): Promise<ActiveDelegation> {
const client = await this.rpc(
"queryLedgerState/rewardAccountSummaries",
{ keys: [rewardAddress] }, // TODO: Does this work for reward addresses that are scripts as well?
"queryLedgerState/delegateRepresentative",
Addresses.inspect(rewardAddress).delegation?.type === "Key"
? { keys: [rewardAddress] }
: { scripts: [rewardAddress] },
);

return new Promise((res, rej) => {
Expand All @@ -175,6 +178,7 @@ export class Kupmios implements Provider {
res(
{
poolId: delegation?.delegate.id || null,
drep: "- information not available -" as ActiveDelegation["drep"], // TODO
rewards: BigInt(delegation?.rewards.ada.lovelace || 0),
},
);
Expand Down
3 changes: 2 additions & 1 deletion src/provider/maestro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,13 @@ export class Maestro implements Provider {
{ headers: this.commonHeaders() },
);
if (!timestampedResultResponse.ok) {
return { poolId: null, rewards: 0n };
return { poolId: null, drep: null, rewards: 0n };
}
const timestampedResult = await timestampedResultResponse.json();
const result = timestampedResult.data;
return {
poolId: result.delegated_pool || null,
drep: "- information not available -" as ActiveDelegation["drep"], // TODO
rewards: BigInt(result.rewards_available),
};
}
Expand Down
Loading

0 comments on commit 57bef49

Please sign in to comment.