diff --git a/package-lock.json b/package-lock.json index 355706e..b7ed6c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@script3/soroban-governor-sdk", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@script3/soroban-governor-sdk", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "dependencies": { "@stellar/stellar-sdk": "12.1.0", diff --git a/package.json b/package.json index 46bf5f1..49378a4 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.1.0", + "version": "1.2.0", "name": "@script3/soroban-governor-sdk", "description": "Javascript SDK for the Soroban Governor", "type": "module", diff --git a/src/calldata_utils.ts b/src/calldata_utils.ts new file mode 100644 index 0000000..7677f7c --- /dev/null +++ b/src/calldata_utils.ts @@ -0,0 +1,61 @@ +import { Address, nativeToScVal, xdr } from "@stellar/stellar-sdk"; +import { Calldata, Val } from "."; + +/** + * Convert Calldata "Vals" to ScVals. This is required for the calldata to be used in the smart contract. + * When using the Governor Client, this is done automatically. + * + * @param calldata + * @returns - The calldata with Vals converted to ScVals + */ +export function convertValsToScVals(calldata: Calldata): any { + return { + args: calldata.args.map((arg) => valToScVal(arg)), + auths: calldata.auths.map((auth) => convertValsToScVals(calldata)), + contract_id: new Address(calldata.contract_id), + function: calldata.function, + }; +} + +/** + * Convert a Val to an ScVal using nativeToScVal. + * + * If you have a complicated object that does not work with nativeToScVal, you + * can either supply a `xdr.ScVal` object directly and the type will be ignored, or, + * you can supply the ScVal as a Base64 XDR string and include `type: { type: "xdr" }` as + * Val's type, and it will be converted to an ScVal directly. + * + * @param val - The Val to convert to an ScVal + * @returns - The xdr ScVal object + */ +export function valToScVal(val: Val): xdr.ScVal { + if (val.type.type === "xdr") { + return xdr.ScVal.fromXDR(val.value, "base64"); + } + return nativeToScVal(val.value, val.type); +} + +/** + * Convert a Calldata object to a SorobanAuthorizedInvocation object. + * @param calldata - The calldata to convert + * @returns - The xdr SorobanAuthorizedInvocation object + */ +export function calldataToAuthInvocation( + calldata: Calldata +): xdr.SorobanAuthorizedInvocation { + let auth_function = + xdr.SorobanAuthorizedFunction.sorobanAuthorizedFunctionTypeContractFn( + new xdr.InvokeContractArgs({ + contractAddress: new Address(calldata.contract_id).toScAddress(), + functionName: calldata.function, + args: calldata.args.map((arg) => valToScVal(arg)), + }) + ); + let subInvocations = calldata.auths.map((auth) => + calldataToAuthInvocation(auth) + ); + return new xdr.SorobanAuthorizedInvocation({ + function: auth_function, + subInvocations: subInvocations, + }); +} diff --git a/src/governor.ts b/src/governor.ts index 62d2997..a6305a8 100644 --- a/src/governor.ts +++ b/src/governor.ts @@ -1,11 +1,6 @@ -import { - Address, - Contract, - contract, - nativeToScVal, - xdr, -} from "@stellar/stellar-sdk"; +import { Address, Contract, contract } from "@stellar/stellar-sdk"; import { Buffer } from "buffer"; +import { convertValsToScVals } from "./calldata_utils.js"; import type { Option, i128, u32 } from "./index.js"; /** @@ -95,54 +90,65 @@ export interface GovernorSettings { /** * This is a wrapper for the XDR type ScVal. It is used to convert between - * a string based representation of the value and the internal XDR type. + * a JS based representation of to the ScVal type used in Soroban. * * See the Stellar SDK's [nativeToScVal](https://stellar.github.io/js-stellar-sdk/ContractSpec.html#nativeToScVal) implementation * for more information. + * + * @param value - The value to convert to an ScVal + * @param type - An object containing a type property that is the ScVal representation. + * @returns The ScVal representation of the value + * + * @example + * - i32 ScVal with value 10 + * ```js + * { + * value: 10, + * type: { + * type: "i32" + * } + * } + * ``` + * + * - Address ScVal with the zero address + * ```js + * { + * value: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + * type: { + * type: "Address" + * } + * } + * ``` + * + * - Complex ScVal encoded as XDR. (This is an example of `GovernorSettings` as an ScVal) + * ```js + * { + * value: "AAAAEQAAAAEAAAAIAAAADwAAAA1jb3VudGluZ190eXBlAAAAAAAAAwAAAAYAAAAPAAAADGdyYWNlX3BlcmlvZAAAAAMAAdiAAAAADwAAABJwcm9wb3NhbF90aHJlc2hvbGQAAAAAAAoAAAAAAAAAAAAAAAJUC+QAAAAADwAAAAZxdW9ydW0AAAAAAAMAAAH0AAAADwAAAAh0aW1lbG9jawAAAAMAAIcAAAAADwAAAAp2b3RlX2RlbGF5AAAAAAADAACHAAAAAA8AAAALdm90ZV9wZXJpb2QAAAAAAwAAyoAAAAAPAAAADnZvdGVfdGhyZXNob2xkAAAAAAADAAAT7A==" + * type: { + * type: "xdr" + * } + * } + * ``` */ -export class Val { - value: string; +export interface Val { + value: any; type: any; - - constructor(value: string, type: any) { - this.value = value; - this.type = type; - } - - toScVal(): xdr.ScVal { - return nativeToScVal(this.value, this.type); - } } /** - * Object for storing call data + * Calldata for a proposal action. Defines a contract action the Governor will take + * when the proposal is executed. + * + * @param args - The arguments for the contract invocation + * @param auths - The authorizations required by the governor for the contract invocation + * @param contract_id - The contract ID of the contract to invoke + * @param function - The function to invoke on the contract */ -export class Calldata { +export interface Calldata { args: Array; auths: Array; contract_id: string; function: string; - - constructor( - contract_id: string, - function_: string, - args: Array, - auths: Array - ) { - this.args = args; - this.auths = auths; - this.contract_id = contract_id; - this.function = function_; - } - - convertValsToScVals(): any { - return { - args: this.args.map((arg) => arg.toScVal()), - auths: this.auths.map((auth) => auth.convertValsToScVals()), - contract_id: new Address(this.contract_id), - function: this.function, - }; - } } /** @@ -382,7 +388,7 @@ export class GovernorContract extends Contract { action: ProposalAction; }): string { if (action.tag === "Calldata") { - let new_value = action.values[0].convertValsToScVals(); + let new_value = convertValsToScVals(action.values[0]); action = { tag: "Calldata", values: [new_value] }; } return this.call( diff --git a/src/index.ts b/src/index.ts index c53cca1..5d54ecc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +export * from "./calldata_utils.js"; export * from "./contract_error.js"; export * from "./contract_result.js"; export * from "./governor.js";