diff --git a/packages/cw/cw.ts b/packages/cw/cw.ts index 11c5c83fcf..ba5841d9ad 100644 --- a/packages/cw/cw.ts +++ b/packages/cw/cw.ts @@ -33,6 +33,8 @@ export { encodeSecp256k1Signature } from './cw-identity' export * from './cw-chains' +export * as Staking from './cw-staking' +export { Core } export default class CWCLI extends CLI { diff --git a/packages/namada/Cargo.lock b/packages/namada/Cargo.lock index 873bcafab5..1d79d0c47d 100644 --- a/packages/namada/Cargo.lock +++ b/packages/namada/Cargo.lock @@ -1604,6 +1604,7 @@ dependencies = [ name = "fadroma-namada" version = "0.1.0" dependencies = [ + "hex", "js-sys", "masp_primitives 1.0.0 (git+https://github.com/anoma/masp?tag=v1.1.0)", "masp_proofs 1.0.0 (git+https://github.com/anoma/masp?tag=v1.1.0)", diff --git a/packages/namada/Cargo.toml b/packages/namada/Cargo.toml index f5d5562ecc..cf9aae2c4f 100644 --- a/packages/namada/Cargo.toml +++ b/packages/namada/Cargo.toml @@ -35,7 +35,7 @@ wasm-bindgen = "0.2.86" #wasm-bindgen-rayon = { version = "1.0", optional = true } #console_error_panic_hook = "0.1.6" #zeroize = "1.6.0" -#hex = "0.4.3" +hex = "0.4.3" #[dependencies.web-sys] #version = "0.3.4" diff --git a/packages/namada/namada-connection.ts b/packages/namada/namada-connection.ts index ee41e851e3..c937210614 100644 --- a/packages/namada/namada-connection.ts +++ b/packages/namada/namada-connection.ts @@ -1,5 +1,7 @@ -import type { Address } from '@fadroma/agent' -import { CWConnection } from '../cw-connection' +import * as CW from '@fadroma/cw' +//import * as WASM from './pkg/fadroma_namada.js' +//console.log({WASM}) +import init, { Decode } from './pkg/fadroma_namada.js' import { getTotalStaked, getStakingParameters, @@ -25,7 +27,21 @@ import { isPGFSteward } from "./namada-pgf" -export class NamadaConnection extends CWConnection { +export async function connect (optionsWithDecoder: ConstructorParameters[0] & { + decoder: string|URL|Uint8Array +}) { + let { decoder, ...options } = optionsWithDecoder + if (decoder instanceof Uint8Array) { + await init(decoder) + } else if (decoder) { + await init(await fetch(decoder)) + } + return new NamadaConnection(options) +} + +export class NamadaConnection extends CW.Connection { + + decode = Decode getPGFParameters () { return getPGFParameters(this) @@ -39,7 +55,7 @@ export class NamadaConnection extends CWConnection { return getPGFFundings(this) } - isPGFSteward (address: Address) { + isPGFSteward (address: CW.Core.Address) { return isPGFSteward(this) } @@ -68,7 +84,7 @@ export class NamadaConnection extends CWConnection { return getValidatorsBelowCapacity(this) } - getValidator (address: Address) { + getValidator (address: CW.Core.Address) { return getValidator(this, address) } @@ -92,7 +108,7 @@ export class NamadaConnection extends CWConnection { return getTotalStaked(this) } - getValidatorStake(address: Address) { + getValidatorStake(address: CW.Core.Address) { return getValidatorStake(this, address) } } diff --git a/packages/namada/namada-epoch.ts b/packages/namada/namada-epoch.ts index 7f77194bfa..7aeb743e6e 100644 --- a/packages/namada/namada-epoch.ts +++ b/packages/namada/namada-epoch.ts @@ -1,15 +1,8 @@ -import * as Borsher from 'borsher' -import { decode, struct, u64 } from '@hackbg/borshest' - -const Schema = Borsher.BorshSchema +import { decode, u64 } from '@hackbg/borshest' type Connection = { abciQuery: (path: string) => Promise } export async function getCurrentEpoch (connection: Connection) { const binary = await connection.abciQuery("/shell/epoch") - return decode(epochSchema, binary) + return decode(u64, binary) } - -const epochSchema = struct( - ["epoch", u64] -) diff --git a/packages/namada/namada-gov.ts b/packages/namada/namada-gov.ts index cf1b341fcb..5e5c9a5e54 100644 --- a/packages/namada/namada-gov.ts +++ b/packages/namada/namada-gov.ts @@ -2,10 +2,11 @@ import { Core } from '@fadroma/agent' import type { Address } from '@fadroma/agent' import type { Address as NamadaAddress } from './namada-address' import { addr, InternalAddresses, decodeAddressFields } from './namada-address' -import { - decode, Struct, - map, set, vec, option, struct, variants, u256, u64, string, unit -} from '@hackbg/borshest' +import { decode, u64 } from '@hackbg/borshest' +//import { + //decode, Struct, + //map, set, vec, option, struct, variants, u256, u64, string, unit +//} from '@hackbg/borshest' type Connection = { abciQuery: (path: string)=>Promise } @@ -14,21 +15,21 @@ export async function getGovernanceParameters (connection: Connection) { return GovernanceParameters.decode(binary) as GovernanceParameters } -export class GovernanceParameters extends Struct( - ["minProposalFund", u256], - ["maxProposalCodeSize", u64], - ["minProposalVotingPeriod", u64], - ["maxProposalPeriod", u64], - ["maxProposalContentSize", u64], - ["minProposalGraceEpochs", u64], -) { - declare minProposalFund: bigint - declare maxProposalCodeSize: bigint - declare minProposalVotingPeriod: bigint - declare maxProposalPeriod: bigint - declare maxProposalContentSize: bigint - declare minProposalGraceEpochs: bigint -} +//export class GovernanceParameters extends Struct( + //["minProposalFund", u256], + //["maxProposalCodeSize", u64], + //["minProposalVotingPeriod", u64], + //["maxProposalPeriod", u64], + //["maxProposalContentSize", u64], + //["minProposalGraceEpochs", u64], +//) { + //declare minProposalFund: bigint + //declare maxProposalCodeSize: bigint + //declare minProposalVotingPeriod: bigint + //declare maxProposalPeriod: bigint + //declare maxProposalContentSize: bigint + //declare minProposalGraceEpochs: bigint +//} export async function getProposalCount (connection: Connection) { const binary = await connection.abciQuery(`/shell/value/#${InternalAddresses.Governance}/counter`) @@ -50,175 +51,175 @@ export async function getProposalInfo (connection: Connection, id: number) { } } -const addRemove = t => variants( - ["Add", t], - ["Remove", t] -) - -const pgfTarget = variants( - ["Internal", struct( - ["target", addr], - ["amount", u256], - )], - ["Ibc", struct( - ["target", string], - ["amount", u256], - ["portId", string], - ["channelId", string], - )] -) - -export class Proposal extends Struct( - ["id", u64], - ["content", map(string, string)], - ["author", addr], - ["type", variants( - ["Default", option(string)], - ["PGFSteward", set(addRemove(addr))], - ["PGFPayment", set(variants( - ["Continuous", addRemove(pgfTarget)], - ["Retro", pgfTarget], - ))] - )], - ["votingStartEpoch", u64], - ["votingEndEpoch", u64], - ["grace_epoch", u64], -) { - declare id: string - declare content: Map - declare author: string - declare type: unknown - declare votingStartEpoch: bigint - declare votingEndEpoch: bigint - declare graceEpoch: bigint -} - -export class ProposalVotes extends Array { - static fromBorsh = binary => new this( - ...(Borsher.borshDeserialize(Schema.Vec(voteSchema), binary) as Array<{ - validator: NamadaAddress - delegator: NamadaAddress - data: { Yay: {} } | { Nay: {} } | { Abstain: {} } - }>).map(vote => new Vote(vote)) - ) -} - -const votes = new Set(['Yay', 'Nay', 'Abstain']) - -export class Vote { - validator: Address - delegator: Address - data: { Yay: {} } | { Nay: {} } | { Abstain: {} } - constructor ({ validator, delegator, data }) { - if (Object.keys(data).length !== 1) { - throw new Core.Error("vote.data variant must have exactly 1 key") - } - if (!votes.has(Object.keys(data)[0])) { - throw new Core.Error("vote.data variant must be one of: Established, Implicit, Internal") - } - this.data = data - this.validator = validator - this.delegator = delegator - decodeAddressFields(this, ["validator", "delegator"]) - } - get value () { - if (typeof this.data !== 'object' || Object.keys(this.data).length !== 1) { - throw new Core.Error("vote.data variant must be an object of exactly 1 key") - } - const value = Object.keys(this.data)[0] - if (!votes.has(value)) { - throw new Core.Error("vote.data variant must be one of: Established, Implicit, Internal") - } - return value as 'Yay'|'Nay'|'Abstain' - } -} - -const voteValueSchema = variants( - ['Yay', unit], - ['Nay', unit], - ['Abstain', unit], -) - -const voteSchema = struct( - ["validator", addr], - ["delegator", addr], - ["data", voteValueSchema], -) - -const proposalStatusSchema = variants( - ["Pending", unit], - ["OnGoing", unit], - ["Ended", unit], -) - -const percent = (a: bigint, b: bigint) => - ((Number(a * 1000000n / b) / 10000).toFixed(2) + '%').padStart(7) - -export class ProposalResult extends Struct( - ["result", variants( - ["Passed", unit], - ["Rejected", unit], - )], - ["tallyType", variants( - ["TwoThirds", unit], - ["OneHalfOverOneThird", unit], - ["LessOneHalfOverOneThirdNay", unit], - )], - ["totalVotingPower", u256], - ["totalYayPower", u256], - ["totalNayPower", u256], - ["totalAbstainPower", u256], -) { - declare result: - | { Passed: {} } - | { Rejected: {} } - declare tallyType: - | { TwoThirds: {} } - | { OneHalfOverOneThird: {} } - | { LessOneHalfOverOneThirdNay: {} } - declare totalVotingPower: bigint - declare totalYayPower: bigint - declare totalNayPower: bigint - declare totalAbstainPower: bigint - - get turnout () { - return this.totalYayPower + this.totalNayPower + this.totalAbstainPower - } - get turnoutPercent () { - return percent(this.turnout, this.totalVotingPower) - } - get yayPercent () { - return percent(this.totalYayPower, this.turnout) - } - get nayPercent () { - return percent(this.totalNayPower, this.turnout) - } - get abstainPercent () { - return percent(this.totalAbstainPower, this.turnout) - } -} - -export class InitProposal extends Struct() { - print (console) { - throw new Error('print InitProposal: not implemented') - } -} - -export class VoteProposal extends Struct( - ['id', u64], - ['vote', voteValueSchema], - ['voter', addr], - ['delegations', vec(addr)] -) { - declare id: bigint - declare vote - declare voter - declare delegations: unknown[] - print (console) { - console.log(Core.bold(' Decoded VoteProposal:')) - .log(' Proposal ID:', Core.bold(this.id)) - .log(' Vote: ', Core.bold(JSON.stringify(this.vote))) - .log(' Voter: ', Core.bold(JSON.stringify(this.voter))) - .log(' Delegations:', Core.bold(JSON.stringify(this.delegations))) - } -} +//const addRemove = t => variants( + //["Add", t], + //["Remove", t] +//) + +//const pgfTarget = variants( + //["Internal", struct( + //["target", addr], + //["amount", u256], + //)], + //["Ibc", struct( + //["target", string], + //["amount", u256], + //["portId", string], + //["channelId", string], + //)] +//) + +//export class Proposal extends Struct( + //["id", u64], + //["content", map(string, string)], + //["author", addr], + //["type", variants( + //["Default", option(string)], + //["PGFSteward", set(addRemove(addr))], + //["PGFPayment", set(variants( + //["Continuous", addRemove(pgfTarget)], + //["Retro", pgfTarget], + //))] + //)], + //["votingStartEpoch", u64], + //["votingEndEpoch", u64], + //["grace_epoch", u64], +//) { + //declare id: string + //declare content: Map + //declare author: string + //declare type: unknown + //declare votingStartEpoch: bigint + //declare votingEndEpoch: bigint + //declare graceEpoch: bigint +//} + +//export class ProposalVotes extends Array { + //static fromBorsh = binary => new this( + //...(Borsher.borshDeserialize(Schema.Vec(voteSchema), binary) as Array<{ + //validator: NamadaAddress + //delegator: NamadaAddress + //data: { Yay: {} } | { Nay: {} } | { Abstain: {} } + //}>).map(vote => new Vote(vote)) + //) +//} + +//const votes = new Set(['Yay', 'Nay', 'Abstain']) + +//export class Vote { + //validator: Address + //delegator: Address + //data: { Yay: {} } | { Nay: {} } | { Abstain: {} } + //constructor ({ validator, delegator, data }) { + //if (Object.keys(data).length !== 1) { + //throw new Core.Error("vote.data variant must have exactly 1 key") + //} + //if (!votes.has(Object.keys(data)[0])) { + //throw new Core.Error("vote.data variant must be one of: Established, Implicit, Internal") + //} + //this.data = data + //this.validator = validator + //this.delegator = delegator + //decodeAddressFields(this, ["validator", "delegator"]) + //} + //get value () { + //if (typeof this.data !== 'object' || Object.keys(this.data).length !== 1) { + //throw new Core.Error("vote.data variant must be an object of exactly 1 key") + //} + //const value = Object.keys(this.data)[0] + //if (!votes.has(value)) { + //throw new Core.Error("vote.data variant must be one of: Established, Implicit, Internal") + //} + //return value as 'Yay'|'Nay'|'Abstain' + //} +//} + +//const voteValueSchema = variants( + //['Yay', unit], + //['Nay', unit], + //['Abstain', unit], +//) + +//const voteSchema = struct( + //["validator", addr], + //["delegator", addr], + //["data", voteValueSchema], +//) + +//const proposalStatusSchema = variants( + //["Pending", unit], + //["OnGoing", unit], + //["Ended", unit], +//) + +//const percent = (a: bigint, b: bigint) => + //((Number(a * 1000000n / b) / 10000).toFixed(2) + '%').padStart(7) + +//export class ProposalResult extends Struct( + //["result", variants( + //["Passed", unit], + //["Rejected", unit], + //)], + //["tallyType", variants( + //["TwoThirds", unit], + //["OneHalfOverOneThird", unit], + //["LessOneHalfOverOneThirdNay", unit], + //)], + //["totalVotingPower", u256], + //["totalYayPower", u256], + //["totalNayPower", u256], + //["totalAbstainPower", u256], +//) { + //declare result: + //| { Passed: {} } + //| { Rejected: {} } + //declare tallyType: + //| { TwoThirds: {} } + //| { OneHalfOverOneThird: {} } + //| { LessOneHalfOverOneThirdNay: {} } + //declare totalVotingPower: bigint + //declare totalYayPower: bigint + //declare totalNayPower: bigint + //declare totalAbstainPower: bigint + + //get turnout () { + //return this.totalYayPower + this.totalNayPower + this.totalAbstainPower + //} + //get turnoutPercent () { + //return percent(this.turnout, this.totalVotingPower) + //} + //get yayPercent () { + //return percent(this.totalYayPower, this.turnout) + //} + //get nayPercent () { + //return percent(this.totalNayPower, this.turnout) + //} + //get abstainPercent () { + //return percent(this.totalAbstainPower, this.turnout) + //} +//} + +//export class InitProposal extends Struct() { + //print (console) { + //throw new Error('print InitProposal: not implemented') + //} +//} + +//export class VoteProposal extends Struct( + //['id', u64], + //['vote', voteValueSchema], + //['voter', addr], + //['delegations', vec(addr)] +//) { + //declare id: bigint + //declare vote + //declare voter + //declare delegations: unknown[] + //print (console) { + //console.log(Core.bold(' Decoded VoteProposal:')) + //.log(' Proposal ID:', Core.bold(this.id)) + //.log(' Vote: ', Core.bold(JSON.stringify(this.vote))) + //.log(' Voter: ', Core.bold(JSON.stringify(this.voter))) + //.log(' Delegations:', Core.bold(JSON.stringify(this.delegations))) + //} +//} diff --git a/packages/namada/namada-identity.ts b/packages/namada/namada-identity.ts index 01beb68fac..98e89da86e 100644 --- a/packages/namada/namada-identity.ts +++ b/packages/namada/namada-identity.ts @@ -1,4 +1,4 @@ -import { CWMnemonicIdentity } from '../cw-identity' +import * as CW from '@fadroma/cw' const defaults = { coinType: 118, @@ -6,8 +6,8 @@ const defaults = { hdAccountIndex: 0, } -export class NamadaMnemonicIdentity extends CWMnemonicIdentity { - constructor (properties?: { mnemonic?: string } & Partial) { +export class NamadaMnemonicIdentity extends CW.MnemonicIdentity { + constructor (properties?: { mnemonic?: string } & Partial) { super({ ...defaults, ...properties||{} }) } } diff --git a/packages/namada/namada-pgf.ts b/packages/namada/namada-pgf.ts index 85015b0e95..05b9760337 100644 --- a/packages/namada/namada-pgf.ts +++ b/packages/namada/namada-pgf.ts @@ -1,8 +1,8 @@ -import { Core } from '@fadroma/agent' -import * as Borsher from 'borsher' -import type { Address } from './namada-address' -import { addr } from './namada-address' -import { Struct, set, u256, i256, map } from '@hackbg/borshest' +//import { Core } from '@fadroma/agent' +//import * as Borsher from 'borsher' +//import type { Address } from './namada-address' +//import { addr } from './namada-address' +//import { Struct, set, u256, i256, map } from '@hackbg/borshest' type Connection = { abciQuery: (path: string)=>Promise } @@ -11,48 +11,48 @@ export async function getPGFParameters (connection: Connection) { return PGFParameters.decode(binary) as PGFParameters } -class PGFParameters extends Struct( - ["stewards", set(addr)], - ["pgf_inflation_rate", u256], - ["stewards_inflation_rate", u256], -) { - declare stewards: Set
- declare pgfInflationRate: bigint - declare stewardsInflationRate: bigint -} +//class PGFParameters extends Struct( + //["stewards", set(addr)], + //["pgf_inflation_rate", u256], + //["stewards_inflation_rate", u256], +//) { + //declare stewards: Set
+ //declare pgfInflationRate: bigint + //declare stewardsInflationRate: bigint +//} export async function getPGFStewards (connection: Connection) { throw new Error("not implemented") } -class PGFSteward extends Struct() { /*TODO*/ } +//class PGFSteward extends Struct() { [>TODO<] } export async function getPGFFundings (connection: Connection) { throw new Error("not implemented") } -class PGFFunding extends Struct() { /*TODO*/ } +//class PGFFunding extends Struct() { [>TODO<] } export async function isPGFSteward (connection: Connection) { throw new Error("not implemented") } -export class UpdateStewardCommission extends Struct( - ['steward', addr], - ['commission', map(addr, i256)] -) { - declare steward: Address - declare commission: Map -} - -export class ResignSteward extends Struct( - ["steward", addr], -) { - declare steward: Address -} - -export { - PGFParameters as Parameters, - PGFSteward as Steward, - PGFFunding as Funding, -} +//export class UpdateStewardCommission extends Struct( + //['steward', addr], + //['commission', map(addr, i256)] +//) { + //declare steward: Address + //declare commission: Map +//} + +//export class ResignSteward extends Struct( + //["steward", addr], +//) { + //declare steward: Address +//} + +//export { + //PGFParameters as Parameters, + //PGFSteward as Steward, + //PGFFunding as Funding, +//} diff --git a/packages/namada/namada-pos.ts b/packages/namada/namada-pos.ts index 90259692b8..cb13096f39 100644 --- a/packages/namada/namada-pos.ts +++ b/packages/namada/namada-pos.ts @@ -1,80 +1,79 @@ -import * as Borsher from 'borsher' import type { Address } from '@fadroma/agent' import { Core } from '@fadroma/agent' import { addr, InternalAddresses, decodeAddress } from './namada-address' import type { Address as NamadaAddress } from './namada-address' import type { NamadaConnection } from './namada-connection' -import * as Staking from '../cw-staking' -import { - decode, Struct, u8, u64, u128, u256, i256, option, struct, variants, unit, string, array, set -} from '@hackbg/borshest' -import type { - AnyField -} from '@hackbg/borshest' +import { Staking } from '@fadroma/cw' +//import { + //decode, Struct, u8, u64, u128, u256, i256, option, struct, variants, unit, string, array, set +//} from '@hackbg/borshest' +//import type { + //AnyField +//} from '@hackbg/borshest' export async function getStakingParameters (connection: NamadaConnection) { const binary = await connection.abciQuery("/vp/pos/pos_params") return PosParams.decode(binary) as PosParams } -const ownedPosParamsFields: Array<[string, AnyField]> = [ - ["maxValidatorSlots", u64], - ["pipelineLen", u64], - ["unbondingLen", u64], - ["tmVotesPerToken", u256], - ["blockProposerReward", u256], - ["blockVoteReward", u256], - ["maxInflationRate", u256], - ["targetStakedRatio", u256], - ["duplicateVoteMinSlashRate", u256], - ["lightClientAttackMinSlashRate", u256], - ["cubicSlashingWindowLength", u64], - ["validatorStakeThreshold", u256], - ["livenessWindowCheck", u64], - ["livenessThreshold", u256], - ["rewardsGainP", u256], - ["rewardsGainD", u256], -] - -export class PosParams extends Struct( - ["owned", struct(...ownedPosParamsFields)], - ["maxProposalPeriod", u64], -) { - declare maxProposalPeriod: bigint - declare owned: OwnedPosParams - constructor (data) { - super(data) - if (!(this.owned instanceof OwnedPosParams)) { - this.owned = new OwnedPosParams(this.owned) - } - } -} - -class OwnedPosParams extends Struct(...ownedPosParamsFields) { - maxValidatorSlots!: bigint - pipelineLen!: bigint - unbondingLen!: bigint - tmVotesPerToken!: bigint - blockProposerReward!: bigint - blockVoteReward!: bigint - maxInflationRate!: bigint - targetStakedRatio!: bigint - duplicateVoteMinSlashRate!: bigint - lightClientAttackMinSlashRate!: bigint - cubicSlashingWindowLength!: bigint - validatorStakeThreshold!: bigint - livenessWindowCheck!: bigint - livenessThreshold!: bigint - rewardsGainP!: bigint - rewardsGainD!: bigint -} +//const ownedPosParamsFields: Array<[string, AnyField]> = [ + //["maxValidatorSlots", u64], + //["pipelineLen", u64], + //["unbondingLen", u64], + //["tmVotesPerToken", u256], + //["blockProposerReward", u256], + //["blockVoteReward", u256], + //["maxInflationRate", u256], + //["targetStakedRatio", u256], + //["duplicateVoteMinSlashRate", u256], + //["lightClientAttackMinSlashRate", u256], + //["cubicSlashingWindowLength", u64], + //["validatorStakeThreshold", u256], + //["livenessWindowCheck", u64], + //["livenessThreshold", u256], + //["rewardsGainP", u256], + //["rewardsGainD", u256], +//] + +//export class PosParams extends Struct( + //["owned", struct(...ownedPosParamsFields)], + //["maxProposalPeriod", u64], +//) { + //declare maxProposalPeriod: bigint + //declare owned: OwnedPosParams + //constructor (data) { + //super(data) + //if (!(this.owned instanceof OwnedPosParams)) { + //this.owned = new OwnedPosParams(this.owned) + //} + //} +//} + +//class OwnedPosParams extends Struct(...ownedPosParamsFields) { + //maxValidatorSlots!: bigint + //pipelineLen!: bigint + //unbondingLen!: bigint + //tmVotesPerToken!: bigint + //blockProposerReward!: bigint + //blockVoteReward!: bigint + //maxInflationRate!: bigint + //targetStakedRatio!: bigint + //duplicateVoteMinSlashRate!: bigint + //lightClientAttackMinSlashRate!: bigint + //cubicSlashingWindowLength!: bigint + //validatorStakeThreshold!: bigint + //livenessWindowCheck!: bigint + //livenessThreshold!: bigint + //rewardsGainP!: bigint + //rewardsGainD!: bigint +//} export async function getTotalStaked (connection: NamadaConnection) { const binary = await connection.abciQuery("/vp/pos/total_stake") return decode(totalStakeSchema, binary) } -const totalStakeSchema = struct([ "totalStake", u64 ]) +//const totalStakeSchema = struct([ "totalStake", u64 ]) export async function getValidators ( connection: NamadaConnection, @@ -111,56 +110,56 @@ export async function getValidators ( } } -export class NamadaValidator extends Staking.Validator { - static fromNamadaAddress = (namadaAddress: string) => Object.assign(new this({}), { namadaAddress }) - namadaAddress!: Address - metadata!: ValidatorMetaData - commission!: CommissionPair - state!: unknown - stake!: bigint - async fetchDetails (connection: NamadaConnection) { - if (!this.namadaAddress) { - const addressBinary = await connection.abciQuery(`/vp/pos/validator_by_tm_addr/${this.address}`) - this.namadaAddress = decodeAddress(addressBinary.slice(1)) - } - const requests = [ - connection.abciQuery(`/vp/pos/validator/metadata/${this.namadaAddress}`) - .then(binary => this.metadata = ValidatorMetaData.decode(binary) as ValidatorMetaData), - connection.abciQuery(`/vp/pos/validator/commission/${this.namadaAddress}`) - .then(binary => this.commission = CommissionPair.decode(binary) as CommissionPair), - connection.abciQuery(`/vp/pos/validator/state/${this.namadaAddress}`) - .then(binary => this.state = decode(stateSchema, binary)), - connection.abciQuery(`/vp/pos/validator/stake/${this.namadaAddress}`) - .then(binary => this.stake = decode(stakeSchema, binary)), - ] - if (this.namadaAddress && !this.publicKey) { - requests.push(connection.abciQuery(`/vp/pos/validator/consensus_key/${this.namadaAddress}`) - .then(binary => this.publicKey = Core.base16.encode(binary.slice(1)))) - } - if (this.namadaAddress && !this.address) { - connection.log.warn("consensus address when fetching all validators: not implemented") - } - await Promise.all(requests) - return this - } - print (console = new Core.Console()) { - console - .log('Validator: ', Core.bold(this.namadaAddress)) - .log(' Address: ', Core.bold(this.address)) - .log(' Public key: ', Core.bold(this.publicKey)) - .log(' State: ', Core.bold(Object.keys(this.state as object)[0])) - .log(' Stake: ', Core.bold(this.stake)) - .log(' Voting power: ', Core.bold(this.votingPower)) - .log(' Priority: ', Core.bold(this.proposerPriority)) - .log(' Commission: ', Core.bold(this.commission.commissionRate)) - .log(' Max change: ', Core.bold(this.commission.maxCommissionChangePerEpoch), 'per epoch') - .log('Email: ', Core.bold(this.metadata?.email||'')) - .log('Website: ', Core.bold(this.metadata?.website||'')) - .log('Discord: ', Core.bold(this.metadata?.discordHandle||'')) - .log('Avatar: ', Core.bold(this.metadata?.avatar||'')) - .log('Description: ', Core.bold(this.metadata?.description||'')) - } -} +//export class NamadaValidator extends Staking.Validator { + //static fromNamadaAddress = (namadaAddress: string) => Object.assign(new this({}), { namadaAddress }) + //namadaAddress!: Address + //metadata!: ValidatorMetaData + //commission!: CommissionPair + //state!: unknown + //stake!: bigint + //async fetchDetails (connection: NamadaConnection) { + //if (!this.namadaAddress) { + //const addressBinary = await connection.abciQuery(`/vp/pos/validator_by_tm_addr/${this.address}`) + //this.namadaAddress = decodeAddress(addressBinary.slice(1)) + //} + //const requests = [ + //connection.abciQuery(`/vp/pos/validator/metadata/${this.namadaAddress}`) + //.then(binary => this.metadata = ValidatorMetaData.decode(binary) as ValidatorMetaData), + //connection.abciQuery(`/vp/pos/validator/commission/${this.namadaAddress}`) + //.then(binary => this.commission = CommissionPair.decode(binary) as CommissionPair), + //connection.abciQuery(`/vp/pos/validator/state/${this.namadaAddress}`) + //.then(binary => this.state = decode(stateSchema, binary)), + //connection.abciQuery(`/vp/pos/validator/stake/${this.namadaAddress}`) + //.then(binary => this.stake = decode(stakeSchema, binary)), + //] + //if (this.namadaAddress && !this.publicKey) { + //requests.push(connection.abciQuery(`/vp/pos/validator/consensus_key/${this.namadaAddress}`) + //.then(binary => this.publicKey = Core.base16.encode(binary.slice(1)))) + //} + //if (this.namadaAddress && !this.address) { + //connection.log.warn("consensus address when fetching all validators: not implemented") + //} + //await Promise.all(requests) + //return this + //} + //print (console = new Core.Console()) { + //console + //.log('Validator: ', Core.bold(this.namadaAddress)) + //.log(' Address: ', Core.bold(this.address)) + //.log(' Public key: ', Core.bold(this.publicKey)) + //.log(' State: ', Core.bold(Object.keys(this.state as object)[0])) + //.log(' Stake: ', Core.bold(this.stake)) + //.log(' Voting power: ', Core.bold(this.votingPower)) + //.log(' Priority: ', Core.bold(this.proposerPriority)) + //.log(' Commission: ', Core.bold(this.commission.commissionRate)) + //.log(' Max change: ', Core.bold(this.commission.maxCommissionChangePerEpoch), 'per epoch') + //.log('Email: ', Core.bold(this.metadata?.email||'')) + //.log('Website: ', Core.bold(this.metadata?.website||'')) + //.log('Discord: ', Core.bold(this.metadata?.discordHandle||'')) + //.log('Avatar: ', Core.bold(this.metadata?.avatar||'')) + //.log('Description: ', Core.bold(this.metadata?.description||'')) + //} +//} export async function getValidatorAddresses (connection: NamadaConnection): Promise { const binary = await connection.abciQuery("/vp/pos/validator/addresses") @@ -168,7 +167,7 @@ export async function getValidatorAddresses (connection: NamadaConnection): Prom .map(bytes=>decodeAddress(bytes)) } -const getValidatorsSchema = set(addr) +//const getValidatorsSchema = set(addr) export async function getValidatorsConsensus (connection: NamadaConnection) { const binary = await connection.abciQuery("/vp/pos/validator_set/consensus") @@ -196,12 +195,12 @@ export async function getValidatorsBelowCapacity (connection: NamadaConnection) : 0) } -const validatorSetMemberFields: Array<[string, AnyField]> = [ - ["bonded_stake", u256], - ["address", addr], -] +//const validatorSetMemberFields: Array<[string, AnyField]> = [ + //["bonded_stake", u256], + //["address", addr], +//] -const validatorSetSchema = set(struct(...validatorSetMemberFields)) +//const validatorSetSchema = set(struct(...validatorSetMemberFields)) export async function getValidator (connection: NamadaConnection, address: Address) { return await NamadaValidator.fromNamadaAddress(address).fetchDetails(connection) @@ -212,156 +211,156 @@ export async function getValidatorStake(connection: NamadaConnection, address: A return decode(validatorStakeSchema, totalStake) } -const validatorStakeSchema = option(struct([ "stake", u128 ])) - -export class ValidatorMetaData extends Struct( - ["email", string], - ["description", option(string)], - ["website", option(string)], - ["discord_handle", option(string)], - ["avatar", option(string)], -) { - email!: string - description!: string|null - website!: string|null - discordHandle!: string|null - avatar!: string|null -} - -export class CommissionPair extends Struct( - ["commission_rate", u256], - ["max_commission_change_per_epoch", u256], -) { - commissionRate!: bigint - maxCommissionChangePerEpoch!: bigint - constructor (data) { - super(data) - decodeU256Fields(this, [ - 'commissionRate', - 'maxCommissionChangePerEpoch', - ]) - } -} - -const stateSchema = option(variants( - ['Consensus', unit], - ['BelowCapacity', unit], - ['BelowThreshold', unit], - ['Inactive', unit], - ['Jailed', unit], -)) - -const stakeSchema = option(u256) - -const pubkey = option(variants( - ['Ed25519', array(32, u8)], - ['Secp256k1', array(33, u8)], -)) - -export class BecomeValidator extends Struct( - ["address", addr], - ["consensus_key", pubkey], - ["eth_cold_key", pubkey], - ["eth_hot_key", pubkey], - ["protocol_key", pubkey], - ["commission_rate", u256], - ["max_commission_rate_change", u256], - ["email", string], - ["description", option(string)], - ["website", option(string)], - ["discord_handle", option(string)], - ["avatar", option(string)], -) { - address - consensusKey - ethColdKey - ethHotKey - protocolKey - commissionRate - maxCommissionRateChange - email - description - website - discordHandle - avatar -} - -export class Bond extends Struct( - ["validator", addr], - ["amount", u256], - ["source", option(addr)], -) { - validator: Address - amount: bigint - source: null|Address -} - -export class ClaimRewards extends Struct( - ["validator", addr], - ["source", option(addr)], -) { - validator: Address - source: null|Address -} - -export class ConsensusKeyChange extends Struct( - ["validator", addr], - ["consensus_key", pubkey], -) { - validator: Address - consensusKey: unknown -} - -export class CommissionChange extends Struct( - ["validator", addr], - ["new_rate", i256], -) { - validator: Address - newRate: bigint -} - -export class MetaDataChange extends Struct( - ["validator", addr], - ["email", option(string)], - ["description", option(string)], - ["website", option(string)], - ["discord_handle", option(string)], - ["avatar", option(string)], - ["commission_rate", option(i256)], -) { - validator: Address - email: null|string - description: null|string - website: null|string - discordHandle: null|string - avatar: null|string - commissionRate: null|string -} - -export class Redelegation extends Struct( - ["src_validator", addr], - ["dest_validator", addr], - ["owner", addr], - ["amount", i256], -) { - srcValidator: Address - destValidator: Address - owner: Address - amount: bigint -} - -export class Unbond extends Struct() {} - -export class Withdraw extends Struct( - ["validator", addr], - ["source", option(addr)], -) { - validator: Address - source: null|Address -} - -export class DeactivateValidator extends Struct() {} - -export class ReactivateValidator extends Struct() {} - -export class UnjailValidator extends Struct() {} +//const validatorStakeSchema = option(struct([ "stake", u128 ])) + +//export class ValidatorMetaData extends Struct( + //["email", string], + //["description", option(string)], + //["website", option(string)], + //["discord_handle", option(string)], + //["avatar", option(string)], +//) { + //email!: string + //description!: string|null + //website!: string|null + //discordHandle!: string|null + //avatar!: string|null +//} + +//export class CommissionPair extends Struct( + //["commission_rate", u256], + //["max_commission_change_per_epoch", u256], +//) { + //commissionRate!: bigint + //maxCommissionChangePerEpoch!: bigint + //constructor (data) { + //super(data) + //decodeU256Fields(this, [ + //'commissionRate', + //'maxCommissionChangePerEpoch', + //]) + //} +//} + +//const stateSchema = option(variants( + //['Consensus', unit], + //['BelowCapacity', unit], + //['BelowThreshold', unit], + //['Inactive', unit], + //['Jailed', unit], +//)) + +//const stakeSchema = option(u256) + +//const pubkey = option(variants( + //['Ed25519', array(32, u8)], + //['Secp256k1', array(33, u8)], +//)) + +//export class BecomeValidator extends Struct( + //["address", addr], + //["consensus_key", pubkey], + //["eth_cold_key", pubkey], + //["eth_hot_key", pubkey], + //["protocol_key", pubkey], + //["commission_rate", u256], + //["max_commission_rate_change", u256], + //["email", string], + //["description", option(string)], + //["website", option(string)], + //["discord_handle", option(string)], + //["avatar", option(string)], +//) { + //address + //consensusKey + //ethColdKey + //ethHotKey + //protocolKey + //commissionRate + //maxCommissionRateChange + //email + //description + //website + //discordHandle + //avatar +//} + +//export class Bond extends Struct( + //["validator", addr], + //["amount", u256], + //["source", option(addr)], +//) { + //validator: Address + //amount: bigint + //source: null|Address +//} + +//export class ClaimRewards extends Struct( + //["validator", addr], + //["source", option(addr)], +//) { + //validator: Address + //source: null|Address +//} + +//export class ConsensusKeyChange extends Struct( + //["validator", addr], + //["consensus_key", pubkey], +//) { + //validator: Address + //consensusKey: unknown +//} + +//export class CommissionChange extends Struct( + //["validator", addr], + //["new_rate", i256], +//) { + //validator: Address + //newRate: bigint +//} + +//export class MetaDataChange extends Struct( + //["validator", addr], + //["email", option(string)], + //["description", option(string)], + //["website", option(string)], + //["discord_handle", option(string)], + //["avatar", option(string)], + //["commission_rate", option(i256)], +//) { + //validator: Address + //email: null|string + //description: null|string + //website: null|string + //discordHandle: null|string + //avatar: null|string + //commissionRate: null|string +//} + +//export class Redelegation extends Struct( + //["src_validator", addr], + //["dest_validator", addr], + //["owner", addr], + //["amount", i256], +//) { + //srcValidator: Address + //destValidator: Address + //owner: Address + //amount: bigint +//} + +//export class Unbond extends Struct() {} + +//export class Withdraw extends Struct( + //["validator", addr], + //["source", option(addr)], +//) { + //validator: Address + //source: null|Address +//} + +//export class DeactivateValidator extends Struct() {} + +//export class ReactivateValidator extends Struct() {} + +//export class UnjailValidator extends Struct() {} diff --git a/packages/namada/namada-tx.ts b/packages/namada/namada-tx.ts index 5565be64e8..c761d9bd70 100644 --- a/packages/namada/namada-tx.ts +++ b/packages/namada/namada-tx.ts @@ -1,364 +1,364 @@ -import { Core } from '@fadroma/agent' -import * as Borsher from 'borsher' -import { addr, decodeAddress } from './namada-address' -import { - BecomeValidator, - Bond, - ConsensusKeyChange, - CommissionChange, - MetaDataChange, - ClaimRewards, - DeactivateValidator, - ReactivateValidator, - Redelegation, - Unbond, - UnjailValidator, - Withdraw -} from './namada-pos' -import { - ResignSteward, - UpdateStewardCommission -} from './namada-pgf' -import { - InitProposal, - VoteProposal -} from './namada-gov' -import { - toHash, - pubkey, - Section, - CodeSection, - DataSection, - ciphertextSectionFields, - codeSectionFields, - dataSectionFields, - headerFields, - maspBuilderSectionFields, - maspTxSection, - maspTxSectionFields, - protocolTransactionFields, - signatureSectionFields, - wrapperTransactionFields, -} from './namada-tx-section' -import { - decode, - array, - struct, - variants, - variant, - u8, - u64, - u256, - i128, - i256, - option, - unit, - string, - vec, - Struct -} from '@hackbg/borshest' -import type { - Fields -} from '@hackbg/borshest' +//import { Core } from '@fadroma/agent' +//import * as Borsher from 'borsher' +//import { addr, decodeAddress } from './namada-address' +//import { + //BecomeValidator, + //Bond, + //ConsensusKeyChange, + //CommissionChange, + //MetaDataChange, + //ClaimRewards, + //DeactivateValidator, + //ReactivateValidator, + //Redelegation, + //Unbond, + //UnjailValidator, + //Withdraw +//} from './namada-pos' +//import { + //ResignSteward, + //UpdateStewardCommission +//} from './namada-pgf' +//import { + //InitProposal, + //VoteProposal +//} from './namada-gov' +//import { + //toHash, + //pubkey, + //Section, + //CodeSection, + //DataSection, + //ciphertextSectionFields, + //codeSectionFields, + //dataSectionFields, + //headerFields, + //maspBuilderSectionFields, + //maspTxSection, + //maspTxSectionFields, + //protocolTransactionFields, + //signatureSectionFields, + //wrapperTransactionFields, +//} from './namada-tx-section' +//import { + //decode, + //array, + //struct, + //variants, + //variant, + //u8, + //u64, + //u256, + //i128, + //i256, + //option, + //unit, + //string, + //vec, + //Struct +//} from '@hackbg/borshest' +//import type { + //Fields +//} from '@hackbg/borshest' -const txSchema = struct( - ['header', struct(...headerFields)], - ['sections', vec(variants( - ['Data', struct(...dataSectionFields)], - ['ExtraData', struct(...codeSectionFields)], - ['Code', struct(...codeSectionFields)], - ['Signature', struct(...signatureSectionFields)], - ['Ciphertext', struct(...ciphertextSectionFields)], - ['MaspTx', maspTxSection], - ['MaspBuilder', struct(...maspBuilderSectionFields)], - ['Header', struct(...headerFields)] - ))] -) +//const txSchema = struct( + //['header', struct(...headerFields)], + //['sections', vec(variants( + //['Data', struct(...dataSectionFields)], + //['ExtraData', struct(...codeSectionFields)], + //['Code', struct(...codeSectionFields)], + //['Signature', struct(...signatureSectionFields)], + //['Ciphertext', struct(...ciphertextSectionFields)], + //['MaspTx', maspTxSection], + //['MaspBuilder', struct(...maspBuilderSectionFields)], + //['Header', struct(...headerFields)] + //))] +//) export class NamadaTransaction { - static decode = (binary: Uint8Array) => { - const decoded = decode(txSchema, binary) - const { header: { txType, ...header }, sections } = decoded as any - const [name, details] = variant(txType) - switch (name) { - case 'Raw': - return new NamadaRawTransaction(header, details, sections) - case 'Wrapper': - return new NamadaWrapperTransaction(header, details, sections) - case 'Decrypted': - return new NamadaDecryptedTransaction(header, details, sections) - case 'Protocol': - return new NamadaProtocolTransaction(header, details, sections) - } - throw new Core.Error( - `Unknown transaction variant "${String(name)}". Valid are: Raw|Wrapper|Decrypted|Protocol` - ) - } - declare chainId: string - declare expiration: string|null - declare timestamp: string - declare codeHash: string - declare dataHash: string - declare memoHash: string - declare txType: 'Raw'|'Wrapper'|'Decrypted'|'Protocol' - declare sections: Section[] - constructor (header: object, sections: object[]) { - for (const [field] of headerFields) { - if (field === 'txType') continue - this[field] = header[field] - } - for (const field of ['codeHash', 'dataHash', 'memoHash']) { - if (this[field] instanceof Uint8Array) { - this[field] = toHash(this[field]) - } else if (this[field] instanceof Array) { - this[field] = toHash(this[field]) - } - } - this.sections = sections.map(section=>Section.fromDecoded(section)) - } - print (console = new Core.Console()) { - console.log('-', Core.bold(`${this.txType} transaction:`)) - .log(' Chain ID: ', Core.bold(this.chainId)) - .log(' Timestamp: ', Core.bold(this.timestamp)) - .log(' Expiration:', Core.bold(this.expiration)) - .log(' Code hash: ', Core.bold(this.codeHash)) - .log(' Data hash: ', Core.bold(this.dataHash)) - .log(' Memo hash: ', Core.bold(this.memoHash)) - .log(' Sections: ', Core.bold(this.sections?.length)) - } - printSections (console = new Core.Console()) { - console.log(Core.bold(' Sections: ')) - for (const section of this.sections) { - console.log() - section.print(console) - } - } + //static decode = (binary: Uint8Array) => { + //const decoded = decode(txSchema, binary) + //const { header: { txType, ...header }, sections } = decoded as any + //const [name, details] = variant(txType) + //switch (name) { + //case 'Raw': + //return new NamadaRawTransaction(header, details, sections) + //case 'Wrapper': + //return new NamadaWrapperTransaction(header, details, sections) + //case 'Decrypted': + //return new NamadaDecryptedTransaction(header, details, sections) + //case 'Protocol': + //return new NamadaProtocolTransaction(header, details, sections) + //} + //throw new Core.Error( + //`Unknown transaction variant "${String(name)}". Valid are: Raw|Wrapper|Decrypted|Protocol` + //) + //} + //declare chainId: string + //declare expiration: string|null + //declare timestamp: string + //declare codeHash: string + //declare dataHash: string + //declare memoHash: string + //declare txType: 'Raw'|'Wrapper'|'Decrypted'|'Protocol' + //declare sections: Section[] + //constructor (header: object, sections: object[]) { + //for (const [field] of headerFields) { + //if (field === 'txType') continue + //this[field] = header[field] + //} + //for (const field of ['codeHash', 'dataHash', 'memoHash']) { + //if (this[field] instanceof Uint8Array) { + //this[field] = toHash(this[field]) + //} else if (this[field] instanceof Array) { + //this[field] = toHash(this[field]) + //} + //} + //this.sections = sections.map(section=>Section.fromDecoded(section)) + //} + //print (console = new Core.Console()) { + //console.log('-', Core.bold(`${this.txType} transaction:`)) + //.log(' Chain ID: ', Core.bold(this.chainId)) + //.log(' Timestamp: ', Core.bold(this.timestamp)) + //.log(' Expiration:', Core.bold(this.expiration)) + //.log(' Code hash: ', Core.bold(this.codeHash)) + //.log(' Data hash: ', Core.bold(this.dataHash)) + //.log(' Memo hash: ', Core.bold(this.memoHash)) + //.log(' Sections: ', Core.bold(this.sections?.length)) + //} + //printSections (console = new Core.Console()) { + //console.log(Core.bold(' Sections: ')) + //for (const section of this.sections) { + //console.log() + //section.print(console) + //} + //} } export class NamadaRawTransaction extends NamadaTransaction { - txType = 'Raw' as 'Raw' - constructor (header: object, details: object, sections: object[]) { - super(header, sections) - this.txType = 'Raw' - } + //txType = 'Raw' as 'Raw' + //constructor (header: object, details: object, sections: object[]) { + //super(header, sections) + //this.txType = 'Raw' + //} } export class NamadaWrapperTransaction extends NamadaTransaction { - txType = 'Wrapper' as 'Wrapper' - declare fee: { - token: string - amountPerGasUnit: { - amount: bigint, - denomination: number - }, - } - declare pk: string - declare epoch: bigint - declare gasLimit: bigint - declare unshieldSectionHash: string|null - constructor (header: object, details: object, sections: object[]) { - super(header, sections) - Core.assignCamelCase(this, details, wrapperTransactionFields.map(x=>x[0] as string)) - this.txType = 'Wrapper' - } + //txType = 'Wrapper' as 'Wrapper' + //declare fee: { + //token: string + //amountPerGasUnit: { + //amount: bigint, + //denomination: number + //}, + //} + //declare pk: string + //declare epoch: bigint + //declare gasLimit: bigint + //declare unshieldSectionHash: string|null + //constructor (header: object, details: object, sections: object[]) { + //super(header, sections) + //Core.assignCamelCase(this, details, wrapperTransactionFields.map(x=>x[0] as string)) + //this.txType = 'Wrapper' + //} } export class NamadaDecryptedTransaction extends NamadaTransaction { - txType = 'Decrypted' as 'Decrypted' - undecryptable: boolean - constructor (header: object, details: object, sections: object[]) { - super(header, sections) - this.txType = 'Decrypted' - const [name, _] = variant(details) - switch (name) { - case 'Decrypted': - this.undecryptable = false - break - case 'Undecryptable': - this.undecryptable = true - break - default: - throw new Core.Error( - `Invalid decrypted transaction details. Allowed: {"Decrypted":{}}|{"Undecryptable":{}}` - ) - } - } - decodeInner () { - return { print () {} } - if (this.undecryptable) { - throw new Core.Error('This transaction is marked as undecryptable.') - } - let tag - for (const section of this.sections) { - if (section instanceof CodeSection) { - tag = (section as CodeSection).tag - break - } - } - if (!tag) { - throw new Core.Error('Could not find a tagged code section in this transaction.') - } - let binary - for (const section of this.sections) { - if (section instanceof DataSection) { - binary = (section as DataSection).data - break - } - } - //console.log('sections', this.sections) - if (!binary) { - throw new Core.Error('Could not find a binary data section in this transaction.') - } - switch (tag) { - case "tx_become_validator.wasm": - return BecomeValidator.decode(binary) - case "tx_bond.wasm": - return Bond.decode(binary) - case "tx_bridge_pool.wasm": - return BridgePool.decode(binary) - case "tx_change_consensus_key.wasm": - return ConsensusKeyChange.decode(binary) - case "tx_change_validator_commission.wasm": - return CommissionChange.decode(binary) - case "tx_change_validator_metadata.wasm": - return MetaDataChange.decode(binary) - case "tx_claim_rewards.wasm": - return ClaimRewards.decode(binary) - case "tx_deactivate_validator.wasm": - return DeactivateValidator.decode(binary) - case "tx_ibc.wasm": - return IBC.decode(binary) - case "tx_init_account.wasm": - return InitAccount.decode(binary) - case "tx_init_proposal.wasm": - return InitProposal.decode(binary) - case "tx_reactivate_validator.wasm": - return ReactivateValidator.decode(binary) - case "tx_redelegate.wasm": - return Redelegation.decode(binary) - case "tx_resign_steward.wasm": - return ResignSteward.decode(binary) - case "tx_reveal_pk.wasm": - return RevealPK.decode(binary) - case "tx_transfer.wasm": - return Transfer.decode(binary) - case "tx_unbond.wasm": - return Unbond.decode(binary) - case "tx_unjail_validator.wasm": - return UnjailValidator.decode(binary) - case "tx_update_account.wasm": - return UpdateAccount.decode(binary) - case "tx_update_steward_commission.wasm": - return UpdateStewardCommission.decode(binary) - case "tx_vote_proposal.wasm": - return { binary, print (console) { console.log(binary) } } - //return VoteProposal.decode(binary) - case "tx_withdraw.wasm": - return Withdraw.decode(binary) - case "vp_implicit.wasm": - return VPImplicit.decode(binary) - case "vp_user.wasm": - return VPUser.decode(binary) - } - throw new Core.Error(`Unsupported inner transaction type: ${tag}`) - } + //txType = 'Decrypted' as 'Decrypted' + //undecryptable: boolean + //constructor (header: object, details: object, sections: object[]) { + //super(header, sections) + //this.txType = 'Decrypted' + //const [name, _] = variant(details) + //switch (name) { + //case 'Decrypted': + //this.undecryptable = false + //break + //case 'Undecryptable': + //this.undecryptable = true + //break + //default: + //throw new Core.Error( + //`Invalid decrypted transaction details. Allowed: {"Decrypted":{}}|{"Undecryptable":{}}` + //) + //} + //} + //decodeInner () { + //return { print () {} } + //if (this.undecryptable) { + //throw new Core.Error('This transaction is marked as undecryptable.') + //} + //let tag + //for (const section of this.sections) { + //if (section instanceof CodeSection) { + //tag = (section as CodeSection).tag + //break + //} + //} + //if (!tag) { + //throw new Core.Error('Could not find a tagged code section in this transaction.') + //} + //let binary + //for (const section of this.sections) { + //if (section instanceof DataSection) { + //binary = (section as DataSection).data + //break + //} + //} + ////console.log('sections', this.sections) + //if (!binary) { + //throw new Core.Error('Could not find a binary data section in this transaction.') + //} + //switch (tag) { + //case "tx_become_validator.wasm": + //return BecomeValidator.decode(binary) + //case "tx_bond.wasm": + //return Bond.decode(binary) + //case "tx_bridge_pool.wasm": + //return BridgePool.decode(binary) + //case "tx_change_consensus_key.wasm": + //return ConsensusKeyChange.decode(binary) + //case "tx_change_validator_commission.wasm": + //return CommissionChange.decode(binary) + //case "tx_change_validator_metadata.wasm": + //return MetaDataChange.decode(binary) + //case "tx_claim_rewards.wasm": + //return ClaimRewards.decode(binary) + //case "tx_deactivate_validator.wasm": + //return DeactivateValidator.decode(binary) + //case "tx_ibc.wasm": + //return IBC.decode(binary) + //case "tx_init_account.wasm": + //return InitAccount.decode(binary) + //case "tx_init_proposal.wasm": + //return InitProposal.decode(binary) + //case "tx_reactivate_validator.wasm": + //return ReactivateValidator.decode(binary) + //case "tx_redelegate.wasm": + //return Redelegation.decode(binary) + //case "tx_resign_steward.wasm": + //return ResignSteward.decode(binary) + //case "tx_reveal_pk.wasm": + //return RevealPK.decode(binary) + //case "tx_transfer.wasm": + //return Transfer.decode(binary) + //case "tx_unbond.wasm": + //return Unbond.decode(binary) + //case "tx_unjail_validator.wasm": + //return UnjailValidator.decode(binary) + //case "tx_update_account.wasm": + //return UpdateAccount.decode(binary) + //case "tx_update_steward_commission.wasm": + //return UpdateStewardCommission.decode(binary) + //case "tx_vote_proposal.wasm": + //return { binary, print (console) { console.log(binary) } } + ////return VoteProposal.decode(binary) + //case "tx_withdraw.wasm": + //return Withdraw.decode(binary) + //case "vp_implicit.wasm": + //return VPImplicit.decode(binary) + //case "vp_user.wasm": + //return VPUser.decode(binary) + //} + //throw new Core.Error(`Unsupported inner transaction type: ${tag}`) + //} } export class NamadaProtocolTransaction extends NamadaTransaction { - txType = 'Protocol' as 'Protocol' - pk: string - tx: |'EthereumEvents' - |'BridgePool' - |'ValidatorSetUpdate' - |'EthEventsVext' - |'BridgePoolVext' - |'ValSetUpdateVext' - constructor (header: object, details: object, sections: object[]) { - super(header, sections) - Core.assignCamelCase(this, details, protocolTransactionFields.map(x=>x[0] as string)) - this.txType = 'Protocol' - } + //txType = 'Protocol' as 'Protocol' + //pk: string + //tx: |'EthereumEvents' + //|'BridgePool' + //|'ValidatorSetUpdate' + //|'EthEventsVext' + //|'BridgePoolVext' + //|'ValSetUpdateVext' + //constructor (header: object, details: object, sections: object[]) { + //super(header, sections) + //Core.assignCamelCase(this, details, protocolTransactionFields.map(x=>x[0] as string)) + //this.txType = 'Protocol' + //} } -export class InitAccount extends Struct( - ['public_keys', vec(pubkey)], - ['vp_code_hash', array(32, u8)], - ['threshold', u8], -) { - publicKeys - vpCodeHash - threshold - print (console) { - throw new Error('print InitAccount: not implemented') - } -} +//export class InitAccount extends Struct( + //['public_keys', vec(pubkey)], + //['vp_code_hash', array(32, u8)], + //['threshold', u8], +//) { + //publicKeys + //vpCodeHash + //threshold + //print (console) { + //throw new Error('print InitAccount: not implemented') + //} +//} -export class UpdateAccount extends Struct( - ['addr', addr], - ['vp_code_hash', option(array(32, u8))], - ['public_keys', vec(pubkey)], - ['threshold', option(u8)] -) { - print (console) { - throw new Error('print UpdateAccount: not implemented') - } -} +//export class UpdateAccount extends Struct( + //['addr', addr], + //['vp_code_hash', option(array(32, u8))], + //['public_keys', vec(pubkey)], + //['threshold', option(u8)] +//) { + //print (console) { + //throw new Error('print UpdateAccount: not implemented') + //} +//} -export class RevealPK extends Struct() { - print (console) { - throw new Error('print RevealPK: not implemented') - } -} +//export class RevealPK extends Struct() { + //print (console) { + //throw new Error('print RevealPK: not implemented') + //} +//} -export class Transfer extends Struct( - ["source", addr], - ["target", addr], - ["token", addr], - ["amount", struct( - ["amount", i256], - ["denom", u8] - )], - ["key", option(string)], - ["shielded", option(array(32, u8))] -) { - declare source - declare target - declare token - declare amount - declare key - declare shielded - print (console) { - console.log(Core.bold(' Decoded Transfer:')) - .log(' Source: ', Core.bold(this.source)) - .log(' Target: ', Core.bold(this.target)) - .log(' Token: ', Core.bold(this.token)) - .log(' Amount: ', Core.bold(this.amount.amount)) - .log(' Denom: ', Core.bold(this.amount.denom)) - .log(' Key: ', Core.bold(this.key)) - .log(' Shielded:', Core.bold(this.shielded)) - } -} +//export class Transfer extends Struct( + //["source", addr], + //["target", addr], + //["token", addr], + //["amount", struct( + //["amount", i256], + //["denom", u8] + //)], + //["key", option(string)], + //["shielded", option(array(32, u8))] +//) { + //declare source + //declare target + //declare token + //declare amount + //declare key + //declare shielded + //print (console) { + //console.log(Core.bold(' Decoded Transfer:')) + //.log(' Source: ', Core.bold(this.source)) + //.log(' Target: ', Core.bold(this.target)) + //.log(' Token: ', Core.bold(this.token)) + //.log(' Amount: ', Core.bold(this.amount.amount)) + //.log(' Denom: ', Core.bold(this.amount.denom)) + //.log(' Key: ', Core.bold(this.key)) + //.log(' Shielded:', Core.bold(this.shielded)) + //} +//} -export class VPImplicit extends Struct() { - print (console) { - throw new Error('print VPImplicit: not implemented') - } -} +//export class VPImplicit extends Struct() { + //print (console) { + //throw new Error('print VPImplicit: not implemented') + //} +//} -export class VPUser extends Struct() { - print (console) { - throw new Error('print VPUser: not implemented') - } -} +//export class VPUser extends Struct() { + //print (console) { + //throw new Error('print VPUser: not implemented') + //} +//} -export class BridgePool extends Struct() { - print (console) { - throw new Error('print BridgePool: not implemented') - } -} +//export class BridgePool extends Struct() { + //print (console) { + //throw new Error('print BridgePool: not implemented') + //} +//} -export class IBC extends Struct() { - print (console) { - console.warn('decode and print IBC: not implemented') - } -} +//export class IBC extends Struct() { + //print (console) { + //console.warn('decode and print IBC: not implemented') + //} +//} diff --git a/packages/namada/namada.cli.mjs b/packages/namada/namada.cli.mjs old mode 100644 new mode 100755 diff --git a/packages/namada/namada.test.mjs b/packages/namada/namada.test.mjs new file mode 100755 index 0000000000..36e9197cca --- /dev/null +++ b/packages/namada/namada.test.mjs @@ -0,0 +1,23 @@ +#!/usr/bin/env node +/** Fadroma. Copyright (C) 2023 Hack.bg. License: GNU AGPLv3 or custom. + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . **/ +import { Console, bold, colors } from '@hackbg/logs' +import { fileURLToPath } from 'node:url' +import { resolve, dirname } from 'node:path' +import { readFileSync } from 'node:fs' +const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), 'package.json') +const { name, version } = JSON.parse(readFileSync(packageJsonPath)) +console.log(`Starting ${bold(name)} ${version}...`) +console.log(colors.green('█▀▀▀▀ █▀▀▀█ █▀▀▀▄ █▀▀▀█ █▀▀▀█ █▀█▀█ █▀▀▀█')) +console.log(colors.green('█▀▀  █▀▀▀█ █▄▄▄▀ █▀▀▀▄ █▄▄▄█ █ ▀ █ █▀▀▀█')) +console.log(colors.green('l e v e l t h e l a n d s c a p e 2021-∞')) +import * as Dotenv from 'dotenv' +Dotenv.config() +new Console().debug('Compiling TypeScript...') +await import("@ganesha/esbuild") +const t0 = performance.now() +const module = await import("./namada.test.ts") +new Console().debug('Compiled TypeScript in', ((performance.now() - t0)/1000).toFixed(3)+'s') +module.default() + diff --git a/packages/namada/namada.test.ts b/packages/namada/namada.test.ts new file mode 100644 index 0000000000..14bb60870e --- /dev/null +++ b/packages/namada/namada.test.ts @@ -0,0 +1,48 @@ +import * as Namada from './namada' +import { Core } from '@fadroma/cw' +import init, { Decode } from './pkg/fadroma_namada.js' +import { readFileSync } from 'node:fs' + +const console = new Core.Console('test') + +export default async function main () { + const connection = await Namada.connect({ + url: 'https://namada-testnet-rpc.itrocket.net', + decoder: readFileSync('./pkg/fadroma_namada_bg.wasm') + }) + console.log(connection.decode.address(new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]))) + let block + let height = 100000 + do { + block = await connection.getBlock(Number(height)) + height = block.header.height + console.log() + .log('Block:', Core.bold(block.header.height)) + .log('ID: ', Core.bold(block.id)) + .log('Time: ', Core.bold(block.header.time)) + .log(Core.bold('Transactions:')) + for (const i in block.txs) { + //const tx = + //console.log(block.txs[i]) + const binary = block.txs[i].slice(3) + console.log(binary) + console.log(Core.brailleDump(binary)) + console.log(connection.decode.tx(block.txs[i].slice(3))) + const tx = Namada.Transaction.decode(binary) + this.log() + tx.print(this.log) + if (tx instanceof Namada.DecryptedTransaction) { + console.log() + tx.decodeInner().print(this.log) + } + //this.log + //.log() + //.log(JSON.stringify(tx, null, 2)) + } + console.br() + height-- + } while (height > 0) + console.log({block}) +} diff --git a/packages/namada/namada.ts b/packages/namada/namada.ts index 52ebb0966f..c63bcc2741 100644 --- a/packages/namada/namada.ts +++ b/packages/namada/namada.ts @@ -1,7 +1,6 @@ import CLI from '@hackbg/cmds' import { Core } from '@fadroma/agent' import { brailleDump } from '@hackbg/dump' -import { CWConnection, CWBatch } from '@fadroma/cw' import { NamadaConnection } from './namada-connection' import { NamadaMnemonicIdentity } from './namada-identity' import { @@ -11,7 +10,7 @@ import { NamadaDecryptedTransaction, NamadaProtocolTransaction } from './namada-tx' - +export { connect } from './namada-connection' export { NamadaCLI as CLI, NamadaConnection as Connection, diff --git a/packages/namada/package.json b/packages/namada/package.json index 008a401549..56e0b2ea98 100644 --- a/packages/namada/package.json +++ b/packages/namada/package.json @@ -12,7 +12,8 @@ "description": "Client library for Namada with WASM SDK", "dependencies": { "@fadroma/cw": "workspace:*", - "@hackbg/cmds": "workspace:*" + "@hackbg/cmds": "workspace:*", + "@hackbg/borshest": "workspace:*" }, "scripts": { "check": "time tsc --noEmit", diff --git a/packages/namada/src/lib.rs b/packages/namada/src/lib.rs index 2586b7997c..ace033232f 100644 --- a/packages/namada/src/lib.rs +++ b/packages/namada/src/lib.rs @@ -1,20 +1,155 @@ extern crate wasm_bindgen; use wasm_bindgen::prelude::*; -use js_sys::{Uint8Array, JsString, Error}; +use js_sys::{Uint8Array, JsString, Error, Object, Array, Reflect}; use namada::address::Address; use namada::string_encoding::Format; +use namada::governance::parameters::GovernanceParameters; +use namada::tx::{Tx, Section}; +use namada::tx::data::TxType; +use masp_primitives::transaction::Transaction; +use namada::core::borsh::BorshDeserialize; +use namada::storage::KeySeg; #[wasm_bindgen] -pub struct API; +pub struct Decode; #[wasm_bindgen] -impl API { +impl Decode { + #[wasm_bindgen] - pub fn decode_address (source: Uint8Array) -> Result { - let mut bytes: Vec = vec![0u8; source.length() as usize]; - source.copy_to(&mut bytes); - let address = Address::decode_bytes(&bytes) + pub fn address (source: Uint8Array) -> Result { + let address = Address::decode_bytes(&to_bytes(&source)) .map_err(|e|Error::new(&format!("{e}")))?; Ok(address.encode().into()) } + + #[wasm_bindgen] + pub fn tx (source: Uint8Array) -> Result { + let tx = Tx::try_from_slice(&to_bytes(&source)) + .map_err(|e|Error::new(&format!("{e}")))?; + let header = tx.header(); + let result = Object::new(); + populate(&result, &[ + ("chain_id".into(), header.chain_id.as_str().into()), + ("expiration".into(), header.expiration.map(|t|t.to_rfc3339()).into()), + ("timestamp".into(), header.timestamp.to_rfc3339().into()), + ("codeHash".into(), header.code_hash.raw().into()), + ("dataHash".into(), header.data_hash.raw().into()), + ("memoHash".into(), header.memo_hash.raw().into()), + ("txType".into(), match header.tx_type { + TxType::Raw => "Raw", + TxType::Wrapper(_) => "Wrapper", + TxType::Decrypted(_) => "Decrypted", + TxType::Protocol(_) => "Protocol", + }.into()), + ])?; + let sections = Array::new(); + for section_data in tx.sections.iter() { + let section = Object::new(); + match section_data { + Section::Data(data) => populate(§ion, &[ + ("type".into(), "Data".into()), + ("salt".into(), hex::encode_upper(data.salt).into()), + ("data".into(), hex::encode_upper(data.data).into()), + ]), + Section::ExtraData(code) => populate(§ion, &[ + ("type".into(), "ExtraData".into()), + ("salt".into(), hex::encode_upper(code.salt).into()), + ("code".into(), hex::encode_upper(code.code.hash().0).into()), + ("tag".into(), code.tag.into()), + ]), + Section::Code(code) => populate(§ion, &[ + ("type".into(), "Code".into()), + ("salt".into(), hex::encode_upper(code.salt).into()), + ("code".into(), hex::encode_upper(code.code.hash().0).into()), + ("tag".into(), code.tag.into()), + ]), + Section::Signature(signature) => populate(§ion, &[ + ("type".into(), "Signature".into()), + ("targets".into(), { + let targets = Array::new(); + for target in signature.targets.iter() { + targets.push(&hex::encode_upper(target.0).into()); + } + targets + }.into()), + ("signer".into(), match signature.signer { + Signer::Address(address) => { + address.encode() + }, + Signer::PubKeys(pubkeys) => { + let output = Array::new(); + for pubkey in pubkeys.iter() { + output.push(&format!("{pubkey}").into()); + } + output + }, + }.into()), + ("signatures".into(), { + let output = Object::new(); + for (key, value) in signature.signatures.iter() { + Reflect::set(&output, &format!("{key}").into(), &format!("{value}").into())?; + } + output + }.into()), + ]), + Section::Ciphertext(ciphertext) => populate(§ion, &[ + ("type".into(), "Ciphertext".into()), + ]), + Section::MaspTx(transaction) => populate(§ion, &[ + ("type".into(), "MaspTx".into()), + ("txid".into(), transaction.txid().into()), + ("version".into(), transaction.version().into()), + ("consensusBranchId".into(), transaction.consensus_branch_id().into()), + ("lockTime".into(), transaction.lock_time().into()), + ("expiryHeight".into(), transaction.expiry_height().into()), + ("transparentBundle".into(), transaction.transparent_bundle().into()), + ("saplingBundle".into(), transaction.sapling_bundle().into()), + ("digest".into(), transaction.digest().into()), + ("saplingValueBalance".into(), transaction.sapling_value_balance().into()), + ]), + Section::MaspBuilder(masp_builder) => populate(§ion, &[ + ("type".into(), "MaspBuilder".into()), + ("target".into(), masp_builder.hash.into()), + ("asset_types".into(), masp_builder.asset_types.into()), + ("metadata".into(), masp_builder.metadata.into()), + ("builder".into(), masp_builder.builder.into()), + ]), + Section::Header(header) => populate(§ion, &[ + ("type".into(), "Header".into()), + ("chain_id".into(), header.chain_id.as_str().into()), + ("expiration".into(), header.expiration.map(|t|t.to_rfc3339()).into()), + ("timestamp".into(), header.timestamp.to_rfc3339().into()), + ("codeHash".into(), header.code_hash.raw().into()), + ("dataHash".into(), header.data_hash.raw().into()), + ("memoHash".into(), header.memo_hash.raw().into()), + ("txType".into(), match header.tx_type { + TxType::Raw => "Raw", + TxType::Wrapper(_) => "Wrapper", + TxType::Decrypted(_) => "Decrypted", + TxType::Protocol(_) => "Protocol", + }.into()), + ]), + }?; + sections.push(§ion); + } + //Reflect::set(&result, &"txid".into(), &tx.txid().to_string().into())?; + Ok(result) + } + +} + +#[inline] +fn populate (object: &Object, fields: &[(JsValue, JsValue)]) -> Result<(), Error> { + for (key, val) in fields.iter() { + Reflect::set(&object, &key.into(), &val.into())?; + } + Ok(()) +} + +#[inline] +fn to_bytes (source: &Uint8Array) -> Vec { + let mut bytes: Vec = vec![0u8; source.length() as usize]; + source.copy_to(&mut bytes); + bytes } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8393da9a59..868cfb3570 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -546,6 +546,18 @@ importers: specifier: workspace:* version: link:../agent + packages/namada: + dependencies: + '@fadroma/cw': + specifier: workspace:* + version: link:../cw + '@hackbg/borshest': + specifier: workspace:* + version: link:../../toolbox/borshest + '@hackbg/cmds': + specifier: workspace:* + version: link:../../toolbox/cmds + packages/oci: dependencies: '@fadroma/agent':