From 35bc9fec0ed86e59f342dcfdbbeb3620475d3d4f Mon Sep 17 00:00:00 2001 From: "lever.wang.wenjia" Date: Thu, 16 Nov 2023 15:27:52 +0800 Subject: [PATCH] update for aptos --- packages/coin-aptos/package.json | 3 +- packages/coin-aptos/src/AptosWallet.ts | 16 +- packages/coin-aptos/src/aptos_account.ts | 134 +++- packages/coin-aptos/src/client.ts | 64 +- packages/coin-aptos/src/generated/index.ts | 1 + .../src/generated/models/HexEncodedBytes.ts | 12 + packages/coin-aptos/src/hex_string.ts | 212 +++--- .../transaction_builder/aptos_types/abi.ts | 24 +- .../aptos_types/account_address.ts | 52 ++ .../aptos_types/authentication_key.ts | 35 +- .../aptos_types/authenticator.ts | 39 +- .../aptos_types/ed25519.ts | 4 + .../transaction_builder/aptos_types/index.ts | 3 +- .../aptos_types/multi_ed25519.ts | 8 +- .../aptos_types/transaction.ts | 355 ++++++---- .../aptos_types/type_tag.ts | 352 +++++++++- .../src/transaction_builder/bcs/consts.ts | 3 +- .../transaction_builder/bcs/deserializer.ts | 18 +- .../src/transaction_builder/bcs/helper.ts | 65 +- .../src/transaction_builder/bcs/serializer.ts | 34 +- .../src/transaction_builder/bcs/types.ts | 3 +- .../src/transaction_builder/builder.ts | 632 +++++++++--------- .../src/transaction_builder/builder_utils.ts | 378 ++++------- .../src/transaction_builder/move_types.ts | 47 ++ packages/coin-aptos/src/utils/hd-key.ts | 76 +++ packages/coin-aptos/src/utils/index.ts | 2 + .../coin-aptos/src/utils/memoize-decorator.ts | 151 +++++ packages/coin-aptos/src/utils/misc.ts | 6 + packages/coin-aptos/tests/aptos.test.ts | 51 +- 29 files changed, 1881 insertions(+), 899 deletions(-) create mode 100644 packages/coin-aptos/src/generated/index.ts create mode 100644 packages/coin-aptos/src/generated/models/HexEncodedBytes.ts create mode 100644 packages/coin-aptos/src/utils/hd-key.ts create mode 100644 packages/coin-aptos/src/utils/index.ts create mode 100644 packages/coin-aptos/src/utils/memoize-decorator.ts create mode 100644 packages/coin-aptos/src/utils/misc.ts diff --git a/packages/coin-aptos/package.json b/packages/coin-aptos/package.json index 006efce..47fe185 100644 --- a/packages/coin-aptos/package.json +++ b/packages/coin-aptos/package.json @@ -32,7 +32,8 @@ "typescript": "^4.6.2" }, "dependencies": { + "@okxweb3/coin-base": "^1.0.0", "@okxweb3/crypto-lib": "^1.0.0", - "@okxweb3/coin-base": "^1.0.0" + "tweetnacl": "^1.0.3" } } diff --git a/packages/coin-aptos/src/AptosWallet.ts b/packages/coin-aptos/src/AptosWallet.ts index ca3b01b..2962b9d 100644 --- a/packages/coin-aptos/src/AptosWallet.ts +++ b/packages/coin-aptos/src/AptosWallet.ts @@ -30,6 +30,7 @@ import { generateBCSTransaction, mintCoin, offerNFTTokenPayload, + offerNFTTokenPayloadObject, registerCoin, transferCoin, transferPayload, @@ -37,7 +38,7 @@ import { import * as client from './client' export type AptosParam = { - type: "transfer" | "tokenTransfer" | "tokenMint" | "tokenBurn" | "tokenRegister" | "dapp" | "simulate" | "offerNft" | "claimNft" | "offerNft_simulate" | "claimNft_simulate" + type: "transfer" | "tokenTransfer" | "tokenMint" | "tokenBurn" | "tokenRegister" | "dapp" | "simulate" | "offerNft"| "offerNftObject"| "claimNft" | "offerNft_simulate" | "claimNft_simulate" base: AptosBasePram, data: any } @@ -81,6 +82,11 @@ export type AptosOfferNFTParam = { version: string amount: string } +export type AptosOfferNFTObjectParam = { + nftObject: string, + receiver: string, + amount: string +} export type AptosClaimNFTParam = { sender: string, @@ -225,6 +231,14 @@ export class AptosWallet extends BaseWallet { tx = generateBCSSimulateTransaction(account, rawTxn); break } + case "offerNftObject": { + const baseParam = ap.base + const data = ap.data as AptosOfferNFTObjectParam + const payload = offerNFTTokenPayloadObject(HexString.ensure(data.nftObject), HexString.ensure(data.receiver), BigInt(data.amount)) + const rawTxn = createRawTransaction(sender, payload, BigInt(baseParam.sequenceNumber), baseParam.chainId, BigInt(baseParam.maxGasAmount), BigInt(baseParam.gasUnitPrice), BigInt(baseParam.expirationTimestampSecs)) + tx = generateBCSTransaction(account, rawTxn); + break + } case "offerNft": { const baseParam = ap.base const data = ap.data as AptosOfferNFTParam diff --git a/packages/coin-aptos/src/aptos_account.ts b/packages/coin-aptos/src/aptos_account.ts index 9321eb9..ff8d67b 100644 --- a/packages/coin-aptos/src/aptos_account.ts +++ b/packages/coin-aptos/src/aptos_account.ts @@ -1,9 +1,17 @@ import { HexString, MaybeHexString } from "./hex_string"; import { MoveTypes } from "./transaction_builder"; -import { base, signUtil } from '@okxweb3/crypto-lib'; - +import nacl from "tweetnacl"; +import * as bip39 from "@scure/bip39"; +import { bytesToHex } from "@noble/hashes/utils"; +import { sha256 } from "@noble/hashes/sha256"; +import { sha3_256 as sha3Hash } from "@noble/hashes/sha3"; +import { derivePath } from "./utils/hd-key"; +import { Memoize } from "./utils"; +import {AccountAddress, AuthenticationKey, Ed25519PublicKey} from "./transaction_builder/aptos_types"; +import {bcsToBytes} from "./transaction_builder/bcs"; +import {TextEncoder} from "util"; export interface AptosAccountObject { - address?: string; + address?: MoveTypes.HexEncodedBytes; publicKeyHex?: MoveTypes.HexEncodedBytes; privateKeyHex: MoveTypes.HexEncodedBytes; } @@ -15,30 +23,66 @@ export class AptosAccount { /** * A private key and public key, associated with the given account */ - private readonly publicKey: Uint8Array - private readonly privateKey: Uint8Array + readonly signingKey: nacl.SignKeyPair; /** * Address associated with the given account */ private readonly accountAddress: HexString; - // Generate an account from the private key + static fromAptosAccountObject(obj: AptosAccountObject): AptosAccount { + return new AptosAccount(HexString.ensure(obj.privateKeyHex).toUint8Array(), obj.address); + } + static fromPrivateKey(privateKey: HexString): AptosAccount { return new AptosAccount(privateKey.toUint8Array()); } + /** + * Check's if the derive path is valid + */ + static isValidPath(path: string): boolean { + return /^m\/44'\/637'\/[0-9]+'\/[0-9]+'\/[0-9]+'+$/.test(path); + } + + /** + * Creates new account with bip44 path and mnemonics, + * @param path. (e.g. m/44'/637'/0'/0'/0') + * Detailed description: {@link https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki} + * @param mnemonics. + * @returns AptosAccount + */ + static fromDerivePath(path: string, mnemonics: string): AptosAccount { + if (!AptosAccount.isValidPath(path)) { + throw new Error("Invalid derivation path"); + } + + const normalizeMnemonics = mnemonics + .trim() + .split(/\s+/) + .map((part) => part.toLowerCase()) + .join(" "); + + const { key } = derivePath(path, bytesToHex(bip39.mnemonicToSeedSync(normalizeMnemonics))); + + return new AptosAccount(key); + } + /** * Creates new account instance. Constructor allows passing in an address, * to handle account key rotation, where auth_key != public_key * @param privateKeyBytes Private key from which account key pair will be generated. * If not specified, new key pair is going to be created. + * @param address Account address (e.g. 0xe8012714cd17606cee7188a2a365eef3fe760be598750678c8c5954eb548a591). * If not specified, a new one will be generated from public key */ - constructor(privateKeyBytes: Uint8Array) { - this.privateKey = privateKeyBytes - this.publicKey = signUtil.ed25519.publicKeyCreate(privateKeyBytes) - this.accountAddress = HexString.ensure(this.authKey().hex()); + constructor(privateKeyBytes?: Uint8Array | undefined, address?: MaybeHexString) { + if (privateKeyBytes) { + this.signingKey = nacl.sign.keyPair.fromSeed(privateKeyBytes.slice(0, 32)); + } else { + this.signingKey = nacl.sign.keyPair(); + } + this.accountAddress = HexString.ensure(address || this.authKey().hex()); } /** @@ -54,14 +98,46 @@ export class AptosAccount { /** * This key enables account owners to rotate their private key(s) * associated with the account without changing the address that hosts their account. - * See here for more info: {@link https://aptos.dev/basics/basics-accounts#single-signer-authentication} + * See here for more info: {@link https://aptos.dev/concepts/accounts#single-signer-authentication} * @returns Authentication key for the associated account */ + @Memoize() authKey(): HexString { - const hash = base.sha3_256.create(); - hash.update(Buffer.from(this.publicKey)); - hash.update("\x00"); - return new HexString(base.toHex(hash.digest())) + const pubKey = new Ed25519PublicKey(this.signingKey.publicKey); + const authKey = AuthenticationKey.fromEd25519PublicKey(pubKey); + return authKey.derivedAddress(); + } + + /** + * Takes source address and seeds and returns the resource account address + * @param sourceAddress Address used to derive the resource account + * @param seed The seed bytes + * @returns The resource account address + */ + static getResourceAccountAddress(sourceAddress: MaybeHexString, seed: Uint8Array): HexString { + const source = bcsToBytes(AccountAddress.fromHex(sourceAddress)); + + const bytes = new Uint8Array([...source, ...seed, AuthenticationKey.DERIVE_RESOURCE_ACCOUNT_SCHEME]); + + const hash = sha3Hash.create(); + hash.update(bytes); + + return HexString.fromUint8Array(hash.digest()); + } + + /** + * Takes creator address and collection name and returns the collection id hash. + * Collection id hash are generated as sha256 hash of (`creator_address::collection_name`) + * + * @param creatorAddress Collection creator address + * @param collectionName The collection name + * @returns The collection id hash + */ + static getCollectionID(creatorAddress: MaybeHexString, collectionName: string): HexString { + const seed = new TextEncoder().encode(`${creatorAddress}::${collectionName}`); + const hash = sha256.create(); + hash.update(seed); + return HexString.fromUint8Array(hash.digest()); } /** @@ -70,7 +146,7 @@ export class AptosAccount { * @returns The public key for the associated account */ pubKey(): HexString { - return HexString.ensure(base.toHex(this.publicKey)); + return HexString.fromUint8Array(this.signingKey.publicKey); } /** @@ -78,9 +154,9 @@ export class AptosAccount { * @param buffer A buffer to sign * @returns A signature HexString */ - signBuffer(buffer: Buffer): HexString { - const signature = signUtil.ed25519.sign(buffer, this.privateKey); - return HexString.ensure(base.toHex(signature).slice(0, 128)); + signBuffer(buffer: Uint8Array): HexString { + const signature = nacl.sign.detached(buffer, this.signingKey.secretKey); + return HexString.fromUint8Array(signature); } /** @@ -89,10 +165,21 @@ export class AptosAccount { * @returns A signature HexString */ signHexString(hexString: MaybeHexString): HexString { - const toSign = HexString.ensure(hexString).toBuffer(); + const toSign = HexString.ensure(hexString).toUint8Array(); return this.signBuffer(toSign); } + /** + * Verifies the signature of the message with the public key of the account + * @param message a signed message + * @param signature the signature of the message + */ + verifySignature(message: MaybeHexString, signature: MaybeHexString): boolean { + const rawMessage = HexString.ensure(message).toUint8Array(); + const rawSignature = HexString.ensure(signature).toUint8Array(); + return nacl.sign.detached.verify(rawMessage, rawSignature, this.signingKey.publicKey); + } + /** * Derives account address, public key and private key * @returns AptosAccountObject instance. @@ -110,7 +197,12 @@ export class AptosAccount { return { address: this.address().hex(), publicKeyHex: this.pubKey().hex(), - privateKeyHex: HexString.fromUint8Array(this.privateKey.slice(0, 32)).hex(), + privateKeyHex: HexString.fromUint8Array(this.signingKey.secretKey.slice(0, 32)).hex(), }; } } + +// Returns an account address as a HexString given either an AptosAccount or a MaybeHexString. +export function getAddressFromAccountOrAddress(accountOrAddress: AptosAccount | MaybeHexString): HexString { + return accountOrAddress instanceof AptosAccount ? accountOrAddress.address() : HexString.ensure(accountOrAddress); +} \ No newline at end of file diff --git a/packages/coin-aptos/src/client.ts b/packages/coin-aptos/src/client.ts index 87caab6..df58cb2 100644 --- a/packages/coin-aptos/src/client.ts +++ b/packages/coin-aptos/src/client.ts @@ -2,18 +2,22 @@ import { HexString, MaybeHexString } from './hex_string'; import { AptosAccount } from './aptos_account'; import { - TxnBuilderTypes, - TransactionBuilderEd25519, - BCS, buildRawTransactionByABI, ABIBuilderConfig, TransactionBuilder, + TxnBuilderTypes, + TransactionBuilderEd25519, + BCS, buildRawTransactionByABI, ABIBuilderConfig, TransactionBuilder, fetchABI, TransactionBuilderABI, } from './transaction_builder'; import { - SignedTransaction, - TransactionAuthenticatorEd25519, - TransactionPayload, + AccountAddress, + ModuleId, + RawTransaction, + SignedTransaction, StructTag, + TransactionAuthenticatorEd25519, + TransactionPayload, TypeTag, TypeTagParser, } from './transaction_builder/aptos_types'; -import { AnyNumber, Deserializer, Uint64, Uint8 } from './transaction_builder/bcs'; -import { MoveModuleBytecode } from './transaction_builder/move_types'; +import {AnyNumber, bcsToBytes, Deserializer, Uint64, Uint8} from './transaction_builder/bcs'; +import {EntryFunctionId, MoveModuleBytecode, MoveType} from './transaction_builder/move_types'; import { base, signUtil } from '@okxweb3/crypto-lib'; +import {ArgumentABI, EntryFunctionABI, TypeArgumentABI} from "./transaction_builder/aptos_types/abi"; declare const TextEncoder: any; /** @@ -75,10 +79,10 @@ export function simulateTransaction(account: AptosAccount, } -// Move modules must expose script functions for initializing and manipulating resources. The script can then be called from a transaction. +// Move models must expose script functions for initializing and manipulating resources. The script can then be called from a transaction. export function transferPayload(recipientAddress: string | HexString, amount: AnyNumber) { return new TxnBuilderTypes.TransactionPayloadEntryFunction( - TxnBuilderTypes.ScriptFunction.natural( + TxnBuilderTypes.EntryFunction.natural( '0x1::aptos_account', 'transfer', [], @@ -94,7 +98,7 @@ export function registerCoin(tyArg: string) { ); return new TxnBuilderTypes.TransactionPayloadEntryFunction( - TxnBuilderTypes.ScriptFunction.natural("0x1::managed_coin", "register", [token], []), + TxnBuilderTypes.EntryFunction.natural("0x1::managed_coin", "register", [token], []), ); } @@ -105,7 +109,7 @@ export function mintCoin(tyArg: string, receiverAddress: string, amount: AnyNumb ); return new TxnBuilderTypes.TransactionPayloadEntryFunction( - TxnBuilderTypes.ScriptFunction.natural( + TxnBuilderTypes.EntryFunction.natural( "0x1::managed_coin", "mint", [token], @@ -120,7 +124,7 @@ export function burnCoin(tyArg: string, amount: AnyNumber) { ); return new TxnBuilderTypes.TransactionPayloadEntryFunction( - TxnBuilderTypes.ScriptFunction.natural( + TxnBuilderTypes.EntryFunction.natural( "0x1::managed_coin", "burn", [token], @@ -135,7 +139,7 @@ export function transferCoin(tyArg: string, receiverAddress: string, amount: Any ); return new TxnBuilderTypes.TransactionPayloadEntryFunction( - TxnBuilderTypes.ScriptFunction.natural( + TxnBuilderTypes.EntryFunction.natural( "0x1::coin", "transfer", [token], @@ -194,7 +198,7 @@ function serializeVectorBool(vecBool: boolean[]) { export function createNFTCollectionPayload(name: string, description: string, uri: string) { const NUMBER_MAX: number = 9007199254740991; return new TxnBuilderTypes.TransactionPayloadEntryFunction( - TxnBuilderTypes.ScriptFunction.natural( + TxnBuilderTypes.EntryFunction.natural( "0x3::token", "create_collection_script", [], @@ -222,7 +226,7 @@ export function createNFTTokenPayload(account: AptosAccount, serializer.serializeU32AsUleb128(0); return new TxnBuilderTypes.TransactionPayloadEntryFunction( - TxnBuilderTypes.ScriptFunction.natural( + TxnBuilderTypes.EntryFunction.natural( "0x3::token", "create_token_script", [], @@ -253,7 +257,7 @@ export function offerNFTTokenPayload( receiver: HexString, version: bigint, amount: bigint) { return new TxnBuilderTypes.TransactionPayloadEntryFunction( - TxnBuilderTypes.ScriptFunction.natural( + TxnBuilderTypes.EntryFunction.natural( "0x3::token_transfers", "offer_script", [], @@ -269,13 +273,30 @@ export function offerNFTTokenPayload( receiver: HexString, ); } +export function offerNFTTokenPayloadObject( nftObject: HexString, + receiver: HexString, + amount: bigint) { + return new TxnBuilderTypes.TransactionPayloadEntryFunction( + TxnBuilderTypes.EntryFunction.natural( + "0x1::object", + "transfer", + [StructTag.fromString("0x1::object::ObjectCore")], + [ + BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(nftObject.hex())), + BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(receiver.hex())), + BCS.bcsSerializeUint64(amount), + ], + ), + ); +} + export function claimNFTTokenPayload(sender: HexString, creator: HexString, collection_name: string, token_name: string, version: bigint) { return new TxnBuilderTypes.TransactionPayloadEntryFunction( - TxnBuilderTypes.ScriptFunction.natural( + TxnBuilderTypes.EntryFunction.natural( "0x3::token_transfers", "claim_script", [], @@ -297,7 +318,7 @@ export function generateBCSTransaction( ): Uint8Array { const txnBuilder = new TransactionBuilderEd25519( (signingMessage: TxnBuilderTypes.SigningMessage) => { - const sigHexStr = accountFrom.signBuffer(signingMessage); + const sigHexStr = accountFrom.signBuffer(Buffer.from(signingMessage)); return new TxnBuilderTypes.Ed25519Signature(sigHexStr.toUint8Array()); }, accountFrom.pubKey().toUint8Array() @@ -334,7 +355,7 @@ export function createRawTransactionByABI(sender: HexString, sequenceNumber: sequenceNumber, gasUnitPrice: gasUnitPrice, maxGasAmount: maxGasAmount, - expTimestampSec: expirationTimestampSecs, + expSecFromNow: expirationTimestampSecs.toString(), chainId: chainId, } @@ -367,4 +388,5 @@ export function validSignedTransaction(tx: string, skipCheckSig: boolean) { throw Error("signature error") } return transaction; -} \ No newline at end of file +} + diff --git a/packages/coin-aptos/src/generated/index.ts b/packages/coin-aptos/src/generated/index.ts new file mode 100644 index 0000000..0cfcc23 --- /dev/null +++ b/packages/coin-aptos/src/generated/index.ts @@ -0,0 +1 @@ +export type { HexEncodedBytes } from './models/HexEncodedBytes'; \ No newline at end of file diff --git a/packages/coin-aptos/src/generated/models/HexEncodedBytes.ts b/packages/coin-aptos/src/generated/models/HexEncodedBytes.ts new file mode 100644 index 0000000..d4a21ba --- /dev/null +++ b/packages/coin-aptos/src/generated/models/HexEncodedBytes.ts @@ -0,0 +1,12 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +/** + * All bytes (Vec) data is represented as hex-encoded string prefixed with `0x` and fulfilled with + * two hex digits per byte. + * + * Unlike the `Address` type, HexEncodedBytes will not trim any zeros. + * + */ +export type HexEncodedBytes = string; diff --git a/packages/coin-aptos/src/hex_string.ts b/packages/coin-aptos/src/hex_string.ts index 4fd6477..e056832 100644 --- a/packages/coin-aptos/src/hex_string.ts +++ b/packages/coin-aptos/src/hex_string.ts @@ -1,126 +1,126 @@ -import { MoveTypes } from "./transaction_builder"; +import {bytesToHex, hexToBytes} from "@noble/hashes/utils"; +import {HexEncodedBytes} from './generated'; // eslint-disable-next-line no-use-before-define -export type MaybeHexString = HexString | string | MoveTypes.HexEncodedBytes; +export type MaybeHexString = HexString | string | HexEncodedBytes; /** * A util class for working with hex strings. * Hex strings are strings that are prefixed with `0x` */ export class HexString { - /// We want to make sure this hexString has the `0x` hex prefix - private readonly hexString: string; + /// We want to make sure this hexString has the `0x` hex prefix + private readonly hexString: string; - /** - * Creates new hex string from Buffer - * @param buffer A buffer to convert - * @returns New HexString - */ - static fromBuffer(buffer: Buffer): HexString { - return new HexString(buffer.toString("hex")); - } - - /** - * Creates new hex string from Uint8Array - * @param arr Uint8Array to convert - * @returns New HexString - */ - static fromUint8Array(arr: Uint8Array): HexString { - return HexString.fromBuffer(Buffer.from(arr)); - } + /** + * Creates new hex string from Buffer + * @param buffer A buffer to convert + * @returns New HexString + */ + static fromBuffer(buffer: Uint8Array): HexString { + return HexString.fromUint8Array(buffer); + } - /** - * Ensures `hexString` is instance of `HexString` class - * @param hexString String to check - * @returns New HexString if `hexString` is regular string or `hexString` if it is HexString instance - * @example - * ``` - * const regularString = "string"; - * const hexString = new HexString("string"); // "0xstring" - * HexString.ensure(regularString); // "0xstring" - * HexString.ensure(hexString); // "0xstring" - * ``` - */ - static ensure(hexString: MaybeHexString): HexString { - if (typeof hexString === "string") { - return new HexString(hexString); + /** + * Creates new hex string from Uint8Array + * @param arr Uint8Array to convert + * @returns New HexString + */ + static fromUint8Array(arr: Uint8Array): HexString { + return new HexString(bytesToHex(arr)); } - return hexString; - } - /** - * Creates new HexString instance from regular string. If specified string already starts with "0x" prefix, - * it will not add another one - * @param hexString String to convert - * @example - * ``` - * const string = "string"; - * new HexString(string); // "0xstring" - * ``` - */ - constructor(hexString: string | MoveTypes.HexEncodedBytes) { - if (hexString.startsWith("0x")) { - this.hexString = hexString; - } else { - this.hexString = `0x${hexString}`; + /** + * Ensures `hexString` is instance of `HexString` class + * @param hexString String to check + * @returns New HexString if `hexString` is regular string or `hexString` if it is HexString instance + * @example + * ``` + * const regularString = "string"; + * const hexString = new HexString("string"); // "0xstring" + * HexString.ensure(regularString); // "0xstring" + * HexString.ensure(hexString); // "0xstring" + * ``` + */ + static ensure(hexString: MaybeHexString): HexString { + if (typeof hexString === "string") { + return new HexString(hexString); + } + return hexString; } - } - /** - * Getter for inner hexString - * @returns Inner hex string - */ - hex(): string { - return this.hexString; - } + /** + * Creates new HexString instance from regular string. If specified string already starts with "0x" prefix, + * it will not add another one + * @param hexString String to convert + * @example + * ``` + * const string = "string"; + * new HexString(string); // "0xstring" + * ``` + */ + constructor(hexString: string | HexEncodedBytes) { + if (hexString.startsWith("0x")) { + this.hexString = hexString; + } else { + this.hexString = `0x${hexString}`; + } + } - /** - * Getter for inner hexString without prefix - * @returns Inner hex string without prefix - * @example - * ``` - * const hexString = new HexString("string"); // "0xstring" - * hexString.noPrefix(); // "string" - * ``` - */ - noPrefix(): string { - return this.hexString.slice(2); - } + /** + * Getter for inner hexString + * @returns Inner hex string + */ + hex(): string { + return this.hexString; + } - /** - * Overrides default `toString` method - * @returns Inner hex string - */ - toString(): string { - return this.hex(); - } + /** + * Getter for inner hexString without prefix + * @returns Inner hex string without prefix + * @example + * ``` + * const hexString = new HexString("string"); // "0xstring" + * hexString.noPrefix(); // "string" + * ``` + */ + noPrefix(): string { + return this.hexString.slice(2); + } - /** - * Trimmes extra zeroes in the begining of a string - * @returns Inner hexString without leading zeroes - * @example - * ``` - * new HexString("0x000000string").toShortString(); // result = "0xstring" - * ``` - */ - toShortString(): string { - const trimmed = this.hexString.replace(/^0x0*/, ""); - return `0x${trimmed}`; - } + /** + * Overrides default `toString` method + * @returns Inner hex string + */ + toString(): string { + return this.hex(); + } - /** - * Converts hex string to a Buffer in hex encoding - * @returns Buffer from inner hexString without prefix - */ - toBuffer(): Buffer { - return Buffer.from(this.noPrefix(), "hex"); - } + /** + * Trimmes extra zeroes in the begining of a string + * @returns Inner hexString without leading zeroes + * @example + * ``` + * new HexString("0x000000string").toShortString(); // result = "0xstring" + * ``` + */ + toShortString(): string { + const trimmed = this.hexString.replace(/^0x0*/, ""); + return `0x${trimmed}`; + } - /** - * Converts hex string to a Uint8Array - * @returns Uint8Array from inner hexString without prefix - */ - toUint8Array(): Uint8Array { - return Uint8Array.from(this.toBuffer()); - } + /** + * Converts hex string to a Uint8Array + * @returns Uint8Array from inner hexString without prefix + */ + toUint8Array(): Uint8Array { + return Uint8Array.from(hexToBytes(this.noPrefix())); + } + /** + * Converts hex string to a Buffer in hex encoding + * @returns Buffer from inner hexString without prefix + */ + /*toBuffer(): Buffer { + return Buffer.from(this.noPrefix(), "hex"); + }*/ } diff --git a/packages/coin-aptos/src/transaction_builder/aptos_types/abi.ts b/packages/coin-aptos/src/transaction_builder/aptos_types/abi.ts index d25c1d6..9f5d7c2 100644 --- a/packages/coin-aptos/src/transaction_builder/aptos_types/abi.ts +++ b/packages/coin-aptos/src/transaction_builder/aptos_types/abi.ts @@ -41,7 +41,7 @@ export class ArgumentABI { this.type_tag.serialize(serializer); } - static deserialize(deserializer: Deserializer): TypeArgumentABI { + static deserialize(deserializer: Deserializer): ArgumentABI { const name = deserializer.deserializeStr(); const typeTag = TypeTag.deserialize(deserializer); return new ArgumentABI(name, typeTag); @@ -74,11 +74,11 @@ export class TransactionScriptABI extends ScriptABI { * @param args */ constructor( - public readonly name: string, - public readonly doc: string, - public readonly code: Bytes, - public readonly ty_args: Seq, - public readonly args: Seq, + public readonly name: string, + public readonly doc: string, + public readonly code: Bytes, + public readonly ty_args: Seq, + public readonly args: Seq, ) { super(); } @@ -112,11 +112,11 @@ export class EntryFunctionABI extends ScriptABI { * @param args */ constructor( - public readonly name: string, - public readonly module_name: ModuleId, - public readonly doc: string, - public readonly ty_args: Seq, - public readonly args: Seq, + public readonly name: string, + public readonly module_name: ModuleId, + public readonly doc: string, + public readonly ty_args: Seq, + public readonly args: Seq, ) { super(); } @@ -138,4 +138,4 @@ export class EntryFunctionABI extends ScriptABI { const args = deserializeVector(deserializer, ArgumentABI); return new EntryFunctionABI(name, moduleName, doc, tyArgs, args); } -} \ No newline at end of file +} diff --git a/packages/coin-aptos/src/transaction_builder/aptos_types/account_address.ts b/packages/coin-aptos/src/transaction_builder/aptos_types/account_address.ts index be0f1b4..a75b22e 100644 --- a/packages/coin-aptos/src/transaction_builder/aptos_types/account_address.ts +++ b/packages/coin-aptos/src/transaction_builder/aptos_types/account_address.ts @@ -9,11 +9,16 @@ import { HexString, MaybeHexString } from "../../hex_string"; import { Serializer, Deserializer, Bytes } from "../bcs"; +/** + * Exported as TransactionBuilderTypes.AccountAddress + */ export class AccountAddress { static readonly LENGTH: number = 32; readonly address: Bytes; + static CORE_CODE_ADDRESS: AccountAddress = AccountAddress.fromHex("0x1"); + constructor(address: Bytes) { if (address.length !== AccountAddress.LENGTH) { throw new Error("Expected address of length 32"); @@ -50,6 +55,37 @@ export class AccountAddress { return new AccountAddress(res); } + /** + * Checks if the string is a valid AccountAddress + * @param addr Hex string can be with a prefix or without a prefix, + * e.g. '0x1aa' or '1aa'. Hex string will be left padded with 0s if too short. + */ + static isValid(addr: MaybeHexString): boolean { + // At least one zero is required + if (addr === "") { + return false; + } + + let address = HexString.ensure(addr); + + // If an address hex has odd number of digits, padd the hex string with 0 + // e.g. '1aa' would become '01aa'. + if (address.noPrefix().length % 2 !== 0) { + address = new HexString(`0${address.noPrefix()}`); + } + + const addressBytes = address.toUint8Array(); + + return addressBytes.length <= AccountAddress.LENGTH; + } + + /** + * Return a hex string from account Address. + */ + toHexString(): MaybeHexString { + return HexString.fromUint8Array(this.address).hex(); + } + serialize(serializer: Serializer): void { serializer.serializeFixedBytes(this.address); } @@ -57,4 +93,20 @@ export class AccountAddress { static deserialize(deserializer: Deserializer): AccountAddress { return new AccountAddress(deserializer.deserializeFixedBytes(AccountAddress.LENGTH)); } + + /** + * Standardizes an address to the format "0x" followed by 64 lowercase hexadecimal digits. + */ + static standardizeAddress(address: string): string { + // Convert the address to lowercase + const lowercaseAddress = address.toLowerCase(); + // Remove the "0x" prefix if present + const addressWithoutPrefix = lowercaseAddress.startsWith("0x") ? lowercaseAddress.slice(2) : lowercaseAddress; + // Pad the address with leading zeros if necessary + // to ensure it has exactly 64 characters (excluding the "0x" prefix) + const addressWithPadding = addressWithoutPrefix.padStart(64, "0"); + // Return the standardized address with the "0x" prefix + return `0x${addressWithPadding}`; + } } + diff --git a/packages/coin-aptos/src/transaction_builder/aptos_types/authentication_key.ts b/packages/coin-aptos/src/transaction_builder/aptos_types/authentication_key.ts index ec9fd6a..b5b6a1a 100644 --- a/packages/coin-aptos/src/transaction_builder/aptos_types/authentication_key.ts +++ b/packages/coin-aptos/src/transaction_builder/aptos_types/authentication_key.ts @@ -10,11 +10,12 @@ import { HexString } from "../../hex_string"; import { Bytes } from "../bcs"; import { MultiEd25519PublicKey } from "./multi_ed25519"; import { base } from '@okxweb3/crypto-lib'; - +import { sha3_256 as sha3Hash } from "@noble/hashes/sha3"; +import {Ed25519PublicKey} from "./ed25519"; /** * Each account stores an authentication key. Authentication key enables account owners to rotate * their private key(s) associated with the account without changing the address that hosts their account. - * @see {@link * https://aptos.dev/basics/basics-accounts | Account Basics} + * @see {@link * https://aptos.dev/concepts/accounts | Account Basics} * * Account addresses can be derived from AuthenticationKey */ @@ -23,6 +24,10 @@ export class AuthenticationKey { static readonly MULTI_ED25519_SCHEME: number = 1; + static readonly ED25519_SCHEME: number = 0; + + static readonly DERIVE_RESOURCE_ACCOUNT_SCHEME: number = 255; + readonly bytes: Bytes; constructor(bytes: Bytes) { @@ -38,9 +43,29 @@ export class AuthenticationKey { * authenticating the transaction. `0x01` is the 1-byte scheme for multisig. */ static fromMultiEd25519PublicKey(publicKey: MultiEd25519PublicKey): AuthenticationKey { - const bytes = new Uint8Array([...publicKey.toBytes(), AuthenticationKey.MULTI_ED25519_SCHEME]); - const hash = base.sha3_256(bytes); - return new AuthenticationKey(hash); + const pubKeyBytes = publicKey.toBytes(); + + const bytes = new Uint8Array(pubKeyBytes.length + 1); + bytes.set(pubKeyBytes); + bytes.set([AuthenticationKey.MULTI_ED25519_SCHEME], pubKeyBytes.length); + + const hash = sha3Hash.create(); + hash.update(bytes); + + return new AuthenticationKey(hash.digest()); + } + + static fromEd25519PublicKey(publicKey: Ed25519PublicKey): AuthenticationKey { + const pubKeyBytes = publicKey.value; + + const bytes = new Uint8Array(pubKeyBytes.length + 1); + bytes.set(pubKeyBytes); + bytes.set([AuthenticationKey.ED25519_SCHEME], pubKeyBytes.length); + + const hash = sha3Hash.create(); + hash.update(bytes); + + return new AuthenticationKey(hash.digest()); } /** diff --git a/packages/coin-aptos/src/transaction_builder/aptos_types/authenticator.ts b/packages/coin-aptos/src/transaction_builder/aptos_types/authenticator.ts index eb35265..ea1c36b 100644 --- a/packages/coin-aptos/src/transaction_builder/aptos_types/authenticator.ts +++ b/packages/coin-aptos/src/transaction_builder/aptos_types/authenticator.ts @@ -11,7 +11,6 @@ import { Serializer, Deserializer, Seq, deserializeVector, serializeVector } fro import { AccountAddress } from "./account_address"; import { Ed25519PublicKey, Ed25519Signature } from "./ed25519"; import { MultiEd25519PublicKey, MultiEd25519Signature } from "./multi_ed25519"; - export abstract class TransactionAuthenticator { abstract serialize(serializer: Serializer): void; @@ -24,6 +23,8 @@ export abstract class TransactionAuthenticator { return TransactionAuthenticatorMultiEd25519.load(deserializer); case 2: return TransactionAuthenticatorMultiAgent.load(deserializer); + case 3: + return TransactionAuthenticatorFeePayer.load(deserializer); default: throw new Error(`Unknown variant index for TransactionAuthenticator: ${index}`); } @@ -83,9 +84,9 @@ export class TransactionAuthenticatorMultiEd25519 extends TransactionAuthenticat export class TransactionAuthenticatorMultiAgent extends TransactionAuthenticator { constructor( - public readonly sender: AccountAuthenticator, - public readonly secondary_signer_addresses: Seq, - public readonly secondary_signers: Seq, + public readonly sender: AccountAuthenticator, + public readonly secondary_signer_addresses: Seq, + public readonly secondary_signers: Seq, ) { super(); } @@ -105,6 +106,36 @@ export class TransactionAuthenticatorMultiAgent extends TransactionAuthenticator } } +export class TransactionAuthenticatorFeePayer extends TransactionAuthenticator { + constructor( + public readonly sender: AccountAuthenticator, + public readonly secondary_signer_addresses: Seq, + public readonly secondary_signers: Seq, + public readonly fee_payer: { address: AccountAddress; authenticator: AccountAuthenticator }, + ) { + super(); + } + + serialize(serializer: Serializer): void { + serializer.serializeU32AsUleb128(3); + this.sender.serialize(serializer); + serializeVector(this.secondary_signer_addresses, serializer); + serializeVector(this.secondary_signers, serializer); + this.fee_payer.address.serialize(serializer); + this.fee_payer.authenticator.serialize(serializer); + } + + static load(deserializer: Deserializer): TransactionAuthenticatorMultiAgent { + const sender = AccountAuthenticator.deserialize(deserializer); + const secondary_signer_addresses = deserializeVector(deserializer, AccountAddress); + const secondary_signers = deserializeVector(deserializer, AccountAuthenticator); + const address = AccountAddress.deserialize(deserializer); + const authenticator = AccountAuthenticator.deserialize(deserializer); + const fee_payer = { address, authenticator }; + return new TransactionAuthenticatorFeePayer(sender, secondary_signer_addresses, secondary_signers, fee_payer); + } +} + export abstract class AccountAuthenticator { abstract serialize(serializer: Serializer): void; diff --git a/packages/coin-aptos/src/transaction_builder/aptos_types/ed25519.ts b/packages/coin-aptos/src/transaction_builder/aptos_types/ed25519.ts index fe5dd7c..44e16e0 100644 --- a/packages/coin-aptos/src/transaction_builder/aptos_types/ed25519.ts +++ b/packages/coin-aptos/src/transaction_builder/aptos_types/ed25519.ts @@ -20,6 +20,10 @@ export class Ed25519PublicKey { this.value = value; } + toBytes(): Bytes { + return this.value; + } + serialize(serializer: Serializer): void { serializer.serializeBytes(this.value); } diff --git a/packages/coin-aptos/src/transaction_builder/aptos_types/index.ts b/packages/coin-aptos/src/transaction_builder/aptos_types/index.ts index 1608280..677fbf1 100644 --- a/packages/coin-aptos/src/transaction_builder/aptos_types/index.ts +++ b/packages/coin-aptos/src/transaction_builder/aptos_types/index.ts @@ -15,4 +15,5 @@ export * from "./ed25519"; export * from "./multi_ed25519"; export * from "./authentication_key"; -export type SigningMessage = Buffer; +// export type SigningMessage = Buffer; +export type SigningMessage = Uint8Array; diff --git a/packages/coin-aptos/src/transaction_builder/aptos_types/multi_ed25519.ts b/packages/coin-aptos/src/transaction_builder/aptos_types/multi_ed25519.ts index 4d5f8b3..6ba7ef6 100644 --- a/packages/coin-aptos/src/transaction_builder/aptos_types/multi_ed25519.ts +++ b/packages/coin-aptos/src/transaction_builder/aptos_types/multi_ed25519.ts @@ -57,8 +57,8 @@ export class MultiEd25519PublicKey { const keys: Seq = []; - for (let i = 0; i < bytes.length; i += Ed25519PublicKey.LENGTH) { - const begin = i * Ed25519PublicKey.LENGTH; + for (let i = 0; i < bytes.length - 1; i += Ed25519PublicKey.LENGTH) { + const begin = i; keys.push(new Ed25519PublicKey(bytes.subarray(begin, begin + Ed25519PublicKey.LENGTH))); } return new MultiEd25519PublicKey(keys, threshold); @@ -154,8 +154,8 @@ export class MultiEd25519Signature { const sigs: Seq = []; - for (let i = 0; i < bytes.length; i += Ed25519Signature.LENGTH) { - const begin = i * Ed25519Signature.LENGTH; + for (let i = 0; i < bytes.length - bitmap.length; i += Ed25519Signature.LENGTH) { + const begin = i; sigs.push(new Ed25519Signature(bytes.subarray(begin, begin + Ed25519Signature.LENGTH))); } return new MultiEd25519Signature(sigs, bitmap); diff --git a/packages/coin-aptos/src/transaction_builder/aptos_types/transaction.ts b/packages/coin-aptos/src/transaction_builder/aptos_types/transaction.ts index a45c565..7ae3b1c 100644 --- a/packages/coin-aptos/src/transaction_builder/aptos_types/transaction.ts +++ b/packages/coin-aptos/src/transaction_builder/aptos_types/transaction.ts @@ -10,6 +10,7 @@ /* eslint-disable class-methods-use-this */ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable max-classes-per-file */ +import { sha3_256 as sha3Hash } from "@noble/hashes/sha3"; import { HexString } from "../../hex_string"; import { Deserializer, @@ -20,7 +21,7 @@ import { Uint8, Uint128, deserializeVector, - serializeVector, + serializeVector, Uint16, Uint256, bcsToBytes, } from "../bcs"; import { AccountAddress } from "./account_address"; import { TransactionAuthenticator } from "./authenticator"; @@ -36,7 +37,7 @@ export class RawTransaction { * @param sequence_number Sequence number of this transaction. This must match the sequence number stored in * the sender's account at the time the transaction executes. * @param payload Instructions for the Aptos Blockchain, including publishing a module, - * execute a script function or execute a script payload. + * execute a entry function or execute a script payload. * @param max_gas_amount Maximum total gas to spend for this transaction. The account must have more * than this gas or the transaction will be discarded during validation. * @param gas_unit_price Price to be paid per gas unit. @@ -44,13 +45,13 @@ export class RawTransaction { * @param chain_id The chain ID of the blockchain that this transaction is intended to be run on. */ constructor( - public readonly sender: AccountAddress, - public readonly sequence_number: Uint64, - public readonly payload: TransactionPayload, - public readonly max_gas_amount: Uint64, - public readonly gas_unit_price: Uint64, - public readonly expiration_timestamp_secs: Uint64, - public readonly chain_id: ChainId, + public readonly sender: AccountAddress, + public readonly sequence_number: Uint64, + public readonly payload: TransactionPayload, + public readonly max_gas_amount: Uint64, + public readonly gas_unit_price: Uint64, + public readonly expiration_timestamp_secs: Uint64, + public readonly chain_id: ChainId, ) {} serialize(serializer: Serializer): void { @@ -72,13 +73,13 @@ export class RawTransaction { const expiration_timestamp_secs = deserializer.deserializeU64(); const chain_id = ChainId.deserialize(deserializer); return new RawTransaction( - sender, - sequence_number, - payload, - max_gas_amount, - gas_unit_price, - expiration_timestamp_secs, - chain_id, + sender, + sequence_number, + payload, + max_gas_amount, + gas_unit_price, + expiration_timestamp_secs, + chain_id, ); } } @@ -103,9 +104,9 @@ export class Script { * ``` */ constructor( - public readonly code: Bytes, - public readonly ty_args: Seq, - public readonly args: Seq, + public readonly code: Bytes, + public readonly ty_args: Seq, + public readonly args: Seq, ) {} serialize(serializer: Serializer): void { @@ -122,10 +123,10 @@ export class Script { } } -export class ScriptFunction { +export class EntryFunction { /** * Contains the payload to run a function within a module. - * @param module_name Fullly qualified module name. ModuleId consists of account address and module name. + * @param module_name Fully qualified module name. ModuleId consists of account address and module name. * @param function_name The function to run. * @param ty_args Type arguments that move function requires. * @@ -143,15 +144,15 @@ export class ScriptFunction { * ``` */ constructor( - public readonly module_name: ModuleId, - public readonly function_name: Identifier, - public readonly ty_args: Seq, - public readonly args: Seq, + public readonly module_name: ModuleId, + public readonly function_name: Identifier, + public readonly ty_args: Seq, + public readonly args: Seq, ) {} /** * - * @param module Fully qualified module name in format "AccountAddress::ModuleName" e.g. "0x1::coin" + * @param module Fully qualified module name in format "AccountAddress::module_name" e.g. "0x1::coin" * @param func Function name * @param ty_args Type arguments that move function requires. * @@ -169,8 +170,8 @@ export class ScriptFunction { * ``` * @returns */ - static natural(module: string, func: string, ty_args: Seq, args: Seq): ScriptFunction { - return new ScriptFunction(ModuleId.fromStr(module), new Identifier(func), ty_args, args); + static natural(module: string, func: string, ty_args: Seq, args: Seq): EntryFunction { + return new EntryFunction(ModuleId.fromStr(module), new Identifier(func), ty_args, args); } /** @@ -178,8 +179,8 @@ export class ScriptFunction { * * @deprecated. */ - static natual(module: string, func: string, ty_args: Seq, args: Seq): ScriptFunction { - return ScriptFunction.natural(module, func, ty_args, args); + static natual(module: string, func: string, ty_args: Seq, args: Seq): EntryFunction { + return EntryFunction.natural(module, func, ty_args, args); } serialize(serializer: Serializer): void { @@ -193,7 +194,7 @@ export class ScriptFunction { }); } - static deserialize(deserializer: Deserializer): ScriptFunction { + static deserialize(deserializer: Deserializer): EntryFunction { const module_name = ModuleId.deserialize(deserializer); const function_name = Identifier.deserialize(deserializer); const ty_args = deserializeVector(deserializer, TypeTag); @@ -205,41 +206,83 @@ export class ScriptFunction { } const args = list; - return new ScriptFunction(module_name, function_name, ty_args, args); + return new EntryFunction(module_name, function_name, ty_args, args); } } -export class Module { +export class MultiSigTransactionPayload { /** - * Contains the bytecode of a Move module that can be published to the Aptos chain. - * @param code Move bytecode of a module. + * Contains the payload to run a multisig account transaction. + * @param transaction_payload The payload of the multisig transaction. This can only be EntryFunction for now but + * Script might be supported in the future. */ - constructor(public readonly code: Bytes) {} + constructor(public readonly transaction_payload: EntryFunction) {} serialize(serializer: Serializer): void { - serializer.serializeBytes(this.code); + // We can support multiple types of inner transaction payload in the future. + // For now it's only EntryFunction but if we support more types, we need to serialize with the right enum values + // here + serializer.serializeU32AsUleb128(0); + this.transaction_payload.serialize(serializer); } - static deserialize(deserializer: Deserializer): Module { - const code = deserializer.deserializeBytes(); - return new Module(code); + static deserialize(deserializer: Deserializer): MultiSigTransactionPayload { + // TODO: Support other types of payload beside EntryFunction. + // This is the enum value indicating which type of payload the multisig tx contains. + deserializer.deserializeUleb128AsU32(); + return new MultiSigTransactionPayload(EntryFunction.deserialize(deserializer)); + } +} + +export class MultiSig { + /** + * Contains the payload to run a multisig account transaction. + * @param multisig_address The multisig account address the transaction will be executed as. + * @param transaction_payload The payload of the multisig transaction. This is optional when executing a multisig + * transaction whose payload is already stored on chain. + */ + constructor( + public readonly multisig_address: AccountAddress, + public readonly transaction_payload?: MultiSigTransactionPayload, + ) {} + + serialize(serializer: Serializer): void { + this.multisig_address.serialize(serializer); + // Options are encoded with an extra u8 field before the value - 0x0 is none and 0x1 is present. + // We use serializeBool below to create this prefix value. + if (this.transaction_payload === undefined) { + serializer.serializeBool(false); + } else { + serializer.serializeBool(true); + this.transaction_payload.serialize(serializer); + } + } + + static deserialize(deserializer: Deserializer): MultiSig { + const multisig_address = AccountAddress.deserialize(deserializer); + const payloadPresent = deserializer.deserializeBool(); + let transaction_payload; + if (payloadPresent) { + transaction_payload = MultiSigTransactionPayload.deserialize(deserializer); + } + return new MultiSig(multisig_address, transaction_payload); } } -export class ModuleBundle { +export class Module { /** - * Contains a list of Modules that can be published together. - * @param codes List of modules. + * Contains the bytecode of a Move module that can be published to the Aptos chain. + * @param code Move bytecode of a module. */ - constructor(public readonly codes: Seq) {} + constructor(public readonly code: Bytes) {} serialize(serializer: Serializer): void { - serializeVector(this.codes, serializer); + serializer.serializeBytes(this.code); } - static deserialize(deserializer: Deserializer): ModuleBundle { - const codes = deserializeVector(deserializer, Module); - return new ModuleBundle(codes); + static deserialize(deserializer: Deserializer): Module { + const code = deserializer.deserializeBytes(); + return new Module(code); } } @@ -253,8 +296,7 @@ export class ModuleId { /** * Converts a string literal to a ModuleId - * @param moduleId String literal in format "AcountAddress::ModuleName", - * e.g. "0x01::Coin" + * @param moduleId String literal in format "AccountAddress::module_name", e.g. "0x1::coin" * @returns */ static fromStr(moduleId: string): ModuleId { @@ -331,6 +373,8 @@ export abstract class RawTransactionWithData { switch (index) { case 0: return MultiAgentRawTransaction.load(deserializer); + case 1: + return FeePayerRawTransaction.load(deserializer); default: throw new Error(`Unknown variant index for RawTransactionWithData: ${index}`); } @@ -339,8 +383,8 @@ export abstract class RawTransactionWithData { export class MultiAgentRawTransaction extends RawTransactionWithData { constructor( - public readonly raw_txn: RawTransaction, - public readonly secondary_signer_addresses: Seq, + public readonly raw_txn: RawTransaction, + public readonly secondary_signer_addresses: Seq, ) { super(); } @@ -360,6 +404,32 @@ export class MultiAgentRawTransaction extends RawTransactionWithData { } } +export class FeePayerRawTransaction extends RawTransactionWithData { + constructor( + public readonly raw_txn: RawTransaction, + public readonly secondary_signer_addresses: Seq, + public readonly fee_payer_address: AccountAddress, + ) { + super(); + } + + serialize(serializer: Serializer): void { + // enum variant index + serializer.serializeU32AsUleb128(1); + this.raw_txn.serialize(serializer); + serializeVector(this.secondary_signer_addresses, serializer); + this.fee_payer_address.serialize(serializer); + } + + static load(deserializer: Deserializer): FeePayerRawTransaction { + const rawTxn = RawTransaction.deserialize(deserializer); + const secondarySignerAddresses = deserializeVector(deserializer, AccountAddress); + const feePayerAddress = AccountAddress.deserialize(deserializer); + + return new FeePayerRawTransaction(rawTxn, secondarySignerAddresses, feePayerAddress); + } +} + export abstract class TransactionPayload { abstract serialize(serializer: Serializer): void; @@ -368,9 +438,11 @@ export abstract class TransactionPayload { switch (index) { case 0: return TransactionPayloadScript.load(deserializer); - // TODO: change to 1 once ModuleBundle has been removed from rust + // TODO: change to 1 once ModuleBundle has been removed from rust case 2: return TransactionPayloadEntryFunction.load(deserializer); + case 3: + return TransactionPayloadMultisig.load(deserializer); default: throw new Error(`Unknown variant index for TransactionPayload: ${index}`); } @@ -409,6 +481,22 @@ export class TransactionPayloadEntryFunction extends TransactionPayload { } } +export class TransactionPayloadMultisig extends TransactionPayload { + constructor(public readonly value: MultiSig) { + super(); + } + + serialize(serializer: Serializer): void { + serializer.serializeU32AsUleb128(3); + this.value.serialize(serializer); + } + + static load(deserializer: Deserializer): TransactionPayloadMultisig { + const value = MultiSig.deserialize(deserializer); + return new TransactionPayloadMultisig(value); + } +} + export class ChainId { constructor(public readonly value: Uint8) {} @@ -440,6 +528,12 @@ export abstract class TransactionArgument { return TransactionArgumentU8Vector.load(deserializer); case 5: return TransactionArgumentBool.load(deserializer); + case 6: + return TransactionArgumentU16.load(deserializer); + case 7: + return TransactionArgumentU32.load(deserializer); + case 8: + return TransactionArgumentU256.load(deserializer); default: throw new Error(`Unknown variant index for TransactionArgument: ${index}`); } @@ -462,6 +556,38 @@ export class TransactionArgumentU8 extends TransactionArgument { } } +export class TransactionArgumentU16 extends TransactionArgument { + constructor(public readonly value: Uint16) { + super(); + } + + serialize(serializer: Serializer): void { + serializer.serializeU32AsUleb128(6); + serializer.serializeU16(this.value); + } + + static load(deserializer: Deserializer): TransactionArgumentU16 { + const value = deserializer.deserializeU16(); + return new TransactionArgumentU16(value); + } +} + +export class TransactionArgumentU32 extends TransactionArgument { + constructor(public readonly value: Uint16) { + super(); + } + + serialize(serializer: Serializer): void { + serializer.serializeU32AsUleb128(7); + serializer.serializeU32(this.value); + } + + static load(deserializer: Deserializer): TransactionArgumentU32 { + const value = deserializer.deserializeU32(); + return new TransactionArgumentU32(value); + } +} + export class TransactionArgumentU64 extends TransactionArgument { constructor(public readonly value: Uint64) { super(); @@ -494,6 +620,22 @@ export class TransactionArgumentU128 extends TransactionArgument { } } +export class TransactionArgumentU256 extends TransactionArgument { + constructor(public readonly value: Uint256) { + super(); + } + + serialize(serializer: Serializer): void { + serializer.serializeU32AsUleb128(8); + serializer.serializeU256(this.value); + } + + static load(deserializer: Deserializer): TransactionArgumentU256 { + const value = deserializer.deserializeU256(); + return new TransactionArgumentU256(value); + } +} + export class TransactionArgumentAddress extends TransactionArgument { constructor(public readonly value: AccountAddress) { super(); @@ -542,89 +684,46 @@ export class TransactionArgumentBool extends TransactionArgument { } } -export class EntryFunction { - /** - * Contains the payload to run a function within a module. - * @param module_name Fully qualified module name. ModuleId consists of account address and module name. - * @param function_name The function to run. - * @param ty_args Type arguments that move function requires. - * - * @example - * A coin transfer function has one type argument "CoinType". - * ``` - * public(script) fun transfer(from: &signer, to: address, amount: u64,) - * ``` - * @param args Arugments to the move function. - * - * @example - * A coin transfer function has three arugments "from", "to" and "amount". - * ``` - * public(script) fun transfer(from: &signer, to: address, amount: u64,) - * ``` - */ - constructor( - public readonly module_name: ModuleId, - public readonly function_name: Identifier, - public readonly ty_args: Seq, - public readonly args: Seq, - ) {} +export abstract class Transaction { + abstract serialize(serializer: Serializer): void; - /** - * - * @param module Fully qualified module name in format "AccountAddress::module_name" e.g. "0x1::coin" - * @param func Function name - * @param ty_args Type arguments that move function requires. - * - * @example - * A coin transfer function has one type argument "CoinType". - * ``` - * public(script) fun transfer(from: &signer, to: address, amount: u64,) - * ``` - * @param args Arugments to the move function. - * - * @example - * A coin transfer function has three arugments "from", "to" and "amount". - * ``` - * public(script) fun transfer(from: &signer, to: address, amount: u64,) - * ``` - * @returns - */ - static natural(module: string, func: string, ty_args: Seq, args: Seq): EntryFunction { - return new EntryFunction(ModuleId.fromStr(module), new Identifier(func), ty_args, args); - } + abstract hash(): Bytes; - /** - * `natual` is deprecated, please use `natural` - * - * @deprecated. - */ - static natual(module: string, func: string, ty_args: Seq, args: Seq): EntryFunction { - return EntryFunction.natural(module, func, ty_args, args); + getHashSalt(): Bytes { + const hash = sha3Hash.create(); + hash.update("APTOS::Transaction"); + return hash.digest(); } - serialize(serializer: Serializer): void { - this.module_name.serialize(serializer); - this.function_name.serialize(serializer); - serializeVector(this.ty_args, serializer); + static deserialize(deserializer: Deserializer): Transaction { + const index = deserializer.deserializeUleb128AsU32(); + switch (index) { + case 0: + return UserTransaction.load(deserializer); + default: + throw new Error(`Unknown variant index for Transaction: ${index}`); + } + } +} - serializer.serializeU32AsUleb128(this.args.length); - this.args.forEach((item: Bytes) => { - serializer.serializeBytes(item); - }); +export class UserTransaction extends Transaction { + constructor(public readonly value: SignedTransaction) { + super(); } - static deserialize(deserializer: Deserializer): EntryFunction { - const module_name = ModuleId.deserialize(deserializer); - const function_name = Identifier.deserialize(deserializer); - const ty_args = deserializeVector(deserializer, TypeTag); + hash(): Bytes { + const hash = sha3Hash.create(); + hash.update(this.getHashSalt()); + hash.update(bcsToBytes(this)); + return hash.digest(); + } - const length = deserializer.deserializeUleb128AsU32(); - const list: Seq = []; - for (let i = 0; i < length; i += 1) { - list.push(deserializer.deserializeBytes()); - } + serialize(serializer: Serializer): void { + serializer.serializeU32AsUleb128(0); + this.value.serialize(serializer); + } - const args = list; - return new EntryFunction(module_name, function_name, ty_args, args); + static load(deserializer: Deserializer): UserTransaction { + return new UserTransaction(SignedTransaction.deserialize(deserializer)); } } diff --git a/packages/coin-aptos/src/transaction_builder/aptos_types/type_tag.ts b/packages/coin-aptos/src/transaction_builder/aptos_types/type_tag.ts index d18d0bd..c550a98 100644 --- a/packages/coin-aptos/src/transaction_builder/aptos_types/type_tag.ts +++ b/packages/coin-aptos/src/transaction_builder/aptos_types/type_tag.ts @@ -1,15 +1,11 @@ -/*** - * Copyright © Aptos Foundation - *SPDX-License-Identifier: Apache-2.0 - * - * https://raw.githubusercontent.com/aptos-labs/aptos-core/097ea73b4a78c0166f22a269f27e514dc895afb4/ecosystem/typescript/sdk/LICENSE - * - * */ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable class-methods-use-this */ -import { Deserializer, Seq, Serializer, deserializeVector, serializeVector } from "../bcs"; +/* eslint-disable max-classes-per-file */ import { AccountAddress } from "./account_address"; +import { Deserializer, Seq, Serializer, deserializeVector, serializeVector } from "../bcs"; import { Identifier } from "./identifier"; export abstract class TypeTag { @@ -34,6 +30,12 @@ export abstract class TypeTag { return TypeTagVector.load(deserializer); case 7: return TypeTagStruct.load(deserializer); + case 8: + return TypeTagU16.load(deserializer); + case 9: + return TypeTagU32.load(deserializer); + case 10: + return TypeTagU256.load(deserializer); default: throw new Error(`Unknown variant index for TypeTag: ${index}`); } @@ -45,7 +47,7 @@ export class TypeTagBool extends TypeTag { serializer.serializeU32AsUleb128(0); } - static load(deserializer: Deserializer): TypeTagBool { + static load(_deserializer: Deserializer): TypeTagBool { return new TypeTagBool(); } } @@ -60,6 +62,26 @@ export class TypeTagU8 extends TypeTag { } } +export class TypeTagU16 extends TypeTag { + serialize(serializer: Serializer): void { + serializer.serializeU32AsUleb128(8); + } + + static load(_deserializer: Deserializer): TypeTagU16 { + return new TypeTagU16(); + } +} + +export class TypeTagU32 extends TypeTag { + serialize(serializer: Serializer): void { + serializer.serializeU32AsUleb128(9); + } + + static load(_deserializer: Deserializer): TypeTagU32 { + return new TypeTagU32(); + } +} + export class TypeTagU64 extends TypeTag { serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(2); @@ -80,6 +102,16 @@ export class TypeTagU128 extends TypeTag { } } +export class TypeTagU256 extends TypeTag { + serialize(serializer: Serializer): void { + serializer.serializeU32AsUleb128(10); + } + + static load(_deserializer: Deserializer): TypeTagU256 { + return new TypeTagU256(); + } +} + export class TypeTagAddress extends TypeTag { serialize(serializer: Serializer): void { serializer.serializeU32AsUleb128(4); @@ -130,14 +162,25 @@ export class TypeTagStruct extends TypeTag { const value = StructTag.deserialize(deserializer); return new TypeTagStruct(value); } + + isStringTypeTag(): boolean { + if ( + this.value.module_name.value === "string" && + this.value.name.value === "String" && + this.value.address.toHexString() === AccountAddress.CORE_CODE_ADDRESS.toHexString() + ) { + return true; + } + return false; + } } export class StructTag { constructor( - public readonly address: AccountAddress, - public readonly module_name: Identifier, - public readonly name: Identifier, - public readonly type_args: Seq, + public readonly address: AccountAddress, + public readonly module_name: Identifier, + public readonly name: Identifier, + public readonly type_args: Seq, ) {} /** @@ -147,17 +190,16 @@ export class StructTag { * @returns */ static fromString(structTag: string): StructTag { - // Type args are not supported in string literal - if (structTag.includes("<")) { - throw new Error("Not implemented"); - } - - const parts = structTag.split("::"); - if (parts.length !== 3) { - throw new Error("Invalid struct tag string literal."); - } + // Use the TypeTagParser to parse the string literal into a TypeTagStruct + const typeTagStruct = new TypeTagParser(structTag).parseTypeTag() as TypeTagStruct; - return new StructTag(AccountAddress.fromHex(parts[0]), new Identifier(parts[1]), new Identifier(parts[2]), []); + // Convert and return as a StructTag + return new StructTag( + typeTagStruct.value.address, + typeTagStruct.value.module_name, + typeTagStruct.value.name, + typeTagStruct.value.type_args, + ); } serialize(serializer: Serializer): void { @@ -175,3 +217,267 @@ export class StructTag { return new StructTag(address, moduleName, name, typeArgs); } } + +export const stringStructTag = new StructTag( + AccountAddress.fromHex("0x1"), + new Identifier("string"), + new Identifier("String"), + [], +); + +export function optionStructTag(typeArg: TypeTag): StructTag { + return new StructTag(AccountAddress.fromHex("0x1"), new Identifier("option"), new Identifier("Option"), [typeArg]); +} + +export function objectStructTag(typeArg: TypeTag): StructTag { + return new StructTag(AccountAddress.fromHex("0x1"), new Identifier("object"), new Identifier("Object"), [typeArg]); +} + +function bail(message: string) { + throw new TypeTagParserError(message); +} + +function isWhiteSpace(c: string): boolean { + if (c.match(/\s/)) { + return true; + } + return false; +} + +function isValidAlphabetic(c: string): boolean { + if (c.match(/[_A-Za-z0-9]/g)) { + return true; + } + return false; +} + +// Generic format is T - for example T1, T2, T10 +function isGeneric(c: string): boolean { + if (c.match(/T\d+/g)) { + return true; + } + return false; +} + +type TokenType = string; +type TokenValue = string; +type Token = [TokenType, TokenValue]; + +// Returns Token and Token byte size +function nextToken(tagStr: string, pos: number): [Token, number] { + const c = tagStr[pos]; + if (c === ":") { + if (tagStr.slice(pos, pos + 2) === "::") { + return [["COLON", "::"], 2]; + } + bail("Unrecognized token."); + } else if (c === "<") { + return [["LT", "<"], 1]; + } else if (c === ">") { + return [["GT", ">"], 1]; + } else if (c === ",") { + return [["COMMA", ","], 1]; + } else if (isWhiteSpace(c)) { + let res = ""; + for (let i = pos; i < tagStr.length; i += 1) { + const char = tagStr[i]; + if (isWhiteSpace(char)) { + res = `${res}${char}`; + } else { + break; + } + } + return [["SPACE", res], res.length]; + } else if (isValidAlphabetic(c)) { + let res = ""; + for (let i = pos; i < tagStr.length; i += 1) { + const char = tagStr[i]; + if (isValidAlphabetic(char)) { + res = `${res}${char}`; + } else { + break; + } + } + if (isGeneric(res)) { + return [["GENERIC", res], res.length]; + } + return [["IDENT", res], res.length]; + } + throw new Error("Unrecognized token."); +} + +function tokenize(tagStr: string): Token[] { + let pos = 0; + const tokens = []; + while (pos < tagStr.length) { + const [token, size] = nextToken(tagStr, pos); + if (token[0] !== "SPACE") { + tokens.push(token); + } + pos += size; + } + return tokens; +} + +/** + * Parser to parse a type tag string + */ +export class TypeTagParser { + private readonly tokens: Token[]; + + private readonly typeTags: string[] = []; + + constructor(tagStr: string, typeTags?: string[]) { + this.tokens = tokenize(tagStr); + this.typeTags = typeTags || []; + } + + private consume(targetToken: string) { + const token = this.tokens.shift(); + if (!token || token[1] !== targetToken) { + bail("Invalid type tag."); + } + } + + /** + * Consumes all of an unused generic field, mostly applicable to object + * + * Note: This is recursive. it can be problematic if there's bad input + * @private + */ + private consumeWholeGeneric() { + this.consume("<"); + while (this.tokens[0][1] !== ">") { + // If it is nested, we have to consume another nested generic + if (this.tokens[0][1] === "<") { + this.consumeWholeGeneric(); + } else { + this.tokens.shift(); + } + } + this.consume(">"); + } + + private parseCommaList(endToken: TokenValue, allowTraillingComma: boolean): TypeTag[] { + const res: TypeTag[] = []; + if (this.tokens.length <= 0) { + bail("Invalid type tag."); + } + + while (this.tokens[0][1] !== endToken) { + res.push(this.parseTypeTag()); + + if (this.tokens.length > 0 && this.tokens[0][1] === endToken) { + break; + } + + this.consume(","); + if (this.tokens.length > 0 && this.tokens[0][1] === endToken && allowTraillingComma) { + break; + } + + if (this.tokens.length <= 0) { + bail("Invalid type tag."); + } + } + return res; + } + + parseTypeTag(): TypeTag { + if (this.tokens.length === 0) { + bail("Invalid type tag."); + } + + // Pop left most element out + const [tokenTy, tokenVal] = this.tokens.shift()!; + + if (tokenVal === "u8") { + return new TypeTagU8(); + } + if (tokenVal === "u16") { + return new TypeTagU16(); + } + if (tokenVal === "u32") { + return new TypeTagU32(); + } + if (tokenVal === "u64") { + return new TypeTagU64(); + } + if (tokenVal === "u128") { + return new TypeTagU128(); + } + if (tokenVal === "u256") { + return new TypeTagU256(); + } + if (tokenVal === "bool") { + return new TypeTagBool(); + } + if (tokenVal === "address") { + return new TypeTagAddress(); + } + if (tokenVal === "vector") { + this.consume("<"); + const res = this.parseTypeTag(); + this.consume(">"); + return new TypeTagVector(res); + } + if (tokenVal === "string") { + return new TypeTagStruct(stringStructTag); + } + if (tokenTy === "IDENT" && (tokenVal.startsWith("0x") || tokenVal.startsWith("0X"))) { + const address = AccountAddress.fromHex(tokenVal); + this.consume("::"); + const [moduleTokenTy, module] = this.tokens.shift()!; + if (moduleTokenTy !== "IDENT") { + bail("Invalid type tag."); + } + this.consume("::"); + const [nameTokenTy, name] = this.tokens.shift()!; + if (nameTokenTy !== "IDENT") { + bail("Invalid type tag."); + } + + // Objects can contain either concrete types e.g. 0x1::object::ObjectCore or generics e.g. T + // Neither matter as we can't do type checks, so just the address applies and we consume the entire generic. + // TODO: Support parsing structs that don't come from core code address + if ( + AccountAddress.CORE_CODE_ADDRESS.toHexString() === address.toHexString() && + module === "object" && + name === "Object" + ) { + this.consumeWholeGeneric(); + return new TypeTagAddress(); + } + + let tyTags: TypeTag[] = []; + // Check if the struct has ty args + if (this.tokens.length > 0 && this.tokens[0][1] === "<") { + this.consume("<"); + tyTags = this.parseCommaList(">", true); + this.consume(">"); + } + + const structTag = new StructTag(address, new Identifier(module), new Identifier(name), tyTags); + return new TypeTagStruct(structTag); + } + if (tokenTy === "GENERIC") { + if (this.typeTags.length === 0) { + bail("Can't convert generic type since no typeTags were specified."); + } + // a generic tokenVal has the format of `T`, for example `T1`. + // The digit (i.e 1) indicates the the index of this type in the typeTags array. + // For a tokenVal == T1, should be parsed as the type in typeTags[1] + const idx = parseInt(tokenVal.substring(1), 10); + return new TypeTagParser(this.typeTags[idx]).parseTypeTag(); + } + + throw new Error("Invalid type tag."); + } +} + +export class TypeTagParserError extends Error { + constructor(message: string) { + super(message); + this.name = "TypeTagParserError"; + } +} diff --git a/packages/coin-aptos/src/transaction_builder/bcs/consts.ts b/packages/coin-aptos/src/transaction_builder/bcs/consts.ts index 217ede5..7c851aa 100644 --- a/packages/coin-aptos/src/transaction_builder/bcs/consts.ts +++ b/packages/coin-aptos/src/transaction_builder/bcs/consts.ts @@ -6,7 +6,7 @@ * * */ -import { Uint128, Uint16, Uint32, Uint64, Uint8 } from "./types"; +import {Uint128, Uint16, Uint256, Uint32, Uint64, Uint8} from "./types"; // Upper bound values for uint8, uint16, uint64 and uint128 export const MAX_U8_NUMBER: Uint8 = 2 ** 8 - 1; @@ -14,3 +14,4 @@ export const MAX_U16_NUMBER: Uint16 = 2 ** 16 - 1; export const MAX_U32_NUMBER: Uint32 = 2 ** 32 - 1; export const MAX_U64_BIG_INT: Uint64 = BigInt(2 ** 64) - 1n; export const MAX_U128_BIG_INT: Uint128 = BigInt(2 ** 128) - 1n; +export const MAX_U256_BIG_INT: Uint256 = BigInt(2 ** 256) - BigInt(1); \ No newline at end of file diff --git a/packages/coin-aptos/src/transaction_builder/bcs/deserializer.ts b/packages/coin-aptos/src/transaction_builder/bcs/deserializer.ts index 3cda9c6..53e8872 100644 --- a/packages/coin-aptos/src/transaction_builder/bcs/deserializer.ts +++ b/packages/coin-aptos/src/transaction_builder/bcs/deserializer.ts @@ -8,7 +8,7 @@ /* eslint-disable no-bitwise */ import { MAX_U32_NUMBER } from "./consts"; -import { Bytes, Uint128, Uint16, Uint32, Uint64, Uint8 } from "./types"; +import {Bytes, Uint128, Uint16, Uint256, Uint32, Uint64, Uint8} from "./types"; declare const TextDecoder: any; export class Deserializer { @@ -153,13 +153,26 @@ export class Deserializer { return BigInt((high << BigInt(64)) | low); } + /** + * Deserializes a uint256 number. + * + * BCS layout for "uint256": Thirty-two bytes. Binary format in little-endian representation. + */ + deserializeU256(): Uint256 { + const low = this.deserializeU128(); + const high = this.deserializeU128(); + + // combine the two 128-bit values and return (little endian) + return BigInt((high << BigInt(128)) | low); + } + /** * Deserializes a uleb128 encoded uint32 number. * * BCS use uleb128 encoding in two cases: (1) lengths of variable-length sequences and (2) tags of enum values */ deserializeUleb128AsU32(): Uint32 { - let value: bigint = 0n; + let value: bigint = BigInt(0); let shift = 0; while (value < MAX_U32_NUMBER) { @@ -175,7 +188,6 @@ export class Deserializer { if (value > MAX_U32_NUMBER) { throw new Error("Overflow while parsing uleb128-encoded uint32 value"); } - return Number(value); } } diff --git a/packages/coin-aptos/src/transaction_builder/bcs/helper.ts b/packages/coin-aptos/src/transaction_builder/bcs/helper.ts index 6c5614d..af8ed7b 100644 --- a/packages/coin-aptos/src/transaction_builder/bcs/helper.ts +++ b/packages/coin-aptos/src/transaction_builder/bcs/helper.ts @@ -8,7 +8,8 @@ import { Deserializer } from "./deserializer"; import { Serializer } from "./serializer"; -import { AnyNumber, Bytes, Seq } from "./types"; +import {AnyNumber, Bytes, Seq, Uint16, Uint32, Uint8} from "./types"; + interface Serializable { serialize(serializer: Serializer): void; @@ -24,6 +25,20 @@ export function serializeVector(value: Seq, serialize }); } +/** + * Serializes a vector with specified item serialization function. + * Very dynamic function and bypasses static typechecking. + */ +export function serializeVectorWithFunc(value: any[], func: string): Bytes { + const serializer = new Serializer(); + serializer.serializeU32AsUleb128(value.length); + const f = (serializer as any)[func]; + value.forEach((item) => { + f.call(serializer, item); + }); + return serializer.getBytes(); +} + /** * Deserializes a vector of values. */ @@ -48,8 +63,56 @@ export function bcsSerializeUint64(value: AnyNumber): Bytes { return serializer.getBytes(); } +export function bcsSerializeU8(value: Uint8): Bytes { + const serializer = new Serializer(); + serializer.serializeU8(value); + return serializer.getBytes(); +} + +export function bcsSerializeU16(value: Uint16): Bytes { + const serializer = new Serializer(); + serializer.serializeU16(value); + return serializer.getBytes(); +} + +export function bcsSerializeU32(value: Uint32): Bytes { + const serializer = new Serializer(); + serializer.serializeU32(value); + return serializer.getBytes(); +} + +export function bcsSerializeU128(value: AnyNumber): Bytes { + const serializer = new Serializer(); + serializer.serializeU128(value); + return serializer.getBytes(); +} + +export function bcsSerializeU256(value: AnyNumber): Bytes { + const serializer = new Serializer(); + serializer.serializeU256(value); + return serializer.getBytes(); +} + +export function bcsSerializeBool(value: boolean): Bytes { + const serializer = new Serializer(); + serializer.serializeBool(value); + return serializer.getBytes(); +} + export function bcsSerializeStr(value: string): Bytes { const serializer = new Serializer(); serializer.serializeStr(value); return serializer.getBytes(); } + +export function bcsSerializeBytes(value: Bytes): Bytes { + const serializer = new Serializer(); + serializer.serializeBytes(value); + return serializer.getBytes(); +} + +export function bcsSerializeFixedBytes(value: Bytes): Bytes { + const serializer = new Serializer(); + serializer.serializeFixedBytes(value); + return serializer.getBytes(); +} diff --git a/packages/coin-aptos/src/transaction_builder/bcs/serializer.ts b/packages/coin-aptos/src/transaction_builder/bcs/serializer.ts index 7d6546d..c0238d2 100644 --- a/packages/coin-aptos/src/transaction_builder/bcs/serializer.ts +++ b/packages/coin-aptos/src/transaction_builder/bcs/serializer.ts @@ -7,7 +7,14 @@ * */ /* eslint-disable no-bitwise */ -import { MAX_U128_BIG_INT, MAX_U16_NUMBER, MAX_U32_NUMBER, MAX_U64_BIG_INT, MAX_U8_NUMBER } from "./consts"; +import { + MAX_U128_BIG_INT, + MAX_U16_NUMBER, + MAX_U256_BIG_INT, + MAX_U32_NUMBER, + MAX_U64_BIG_INT, + MAX_U8_NUMBER +} from "./consts"; import { AnyNumber, Bytes, Uint16, Uint32, Uint8 } from "./types"; declare const TextEncoder: any; @@ -36,9 +43,9 @@ export class Serializer { } private serializeWithFunction( - fn: (byteOffset: number, value: number, littleEndian?: boolean) => void, - bytesLength: number, - value: number, + fn: (byteOffset: number, value: number, littleEndian?: boolean) => void, + bytesLength: number, + value: number, ) { this.ensureBufferWillHandleSize(bytesLength); const dv = new DataView(this.buffer, this.offset); @@ -152,7 +159,7 @@ export class Serializer { * assert(serializer.getBytes() === new Uint8Array([0x00, 0xEF, 0xCD, 0xAB, 0x78, 0x56, 0x34, 0x12])); * ``` */ - @checkNumberRange(0n, MAX_U64_BIG_INT) + @checkNumberRange(BigInt(0), MAX_U64_BIG_INT) serializeU64(value: AnyNumber): void { const low = BigInt(value.toString()) & BigInt(MAX_U32_NUMBER); const high = BigInt(value.toString()) >> BigInt(32); @@ -167,7 +174,7 @@ export class Serializer { * * BCS layout for "uint128": Sixteen bytes. Binary format in little-endian representation. */ - @checkNumberRange(0n, MAX_U128_BIG_INT) + @checkNumberRange(BigInt(0), MAX_U128_BIG_INT) serializeU128(value: AnyNumber): void { const low = BigInt(value.toString()) & MAX_U64_BIG_INT; const high = BigInt(value.toString()) >> BigInt(64); @@ -177,6 +184,21 @@ export class Serializer { this.serializeU64(high); } + /** + * Serializes a uint256 number. + * + * BCS layout for "uint256": Sixteen bytes. Binary format in little-endian representation. + */ + @checkNumberRange(BigInt(0), MAX_U256_BIG_INT) + serializeU256(value: AnyNumber): void { + const low = BigInt(value.toString()) & MAX_U128_BIG_INT; + const high = BigInt(value.toString()) >> BigInt(128); + + // write little endian number + this.serializeU128(low); + this.serializeU128(high); + } + /** * Serializes a uint32 number with uleb128. * diff --git a/packages/coin-aptos/src/transaction_builder/bcs/types.ts b/packages/coin-aptos/src/transaction_builder/bcs/types.ts index 47e1bc5..562dbb4 100644 --- a/packages/coin-aptos/src/transaction_builder/bcs/types.ts +++ b/packages/coin-aptos/src/transaction_builder/bcs/types.ts @@ -13,5 +13,6 @@ export type Uint16 = number; export type Uint32 = number; export type Uint64 = bigint; export type Uint128 = bigint; +export type Uint256 = bigint; export type AnyNumber = bigint | number; -export type Bytes = Uint8Array; +export type Bytes = Uint8Array; \ No newline at end of file diff --git a/packages/coin-aptos/src/transaction_builder/builder.ts b/packages/coin-aptos/src/transaction_builder/builder.ts index 440724f..72989da 100644 --- a/packages/coin-aptos/src/transaction_builder/builder.ts +++ b/packages/coin-aptos/src/transaction_builder/builder.ts @@ -5,39 +5,42 @@ * https://raw.githubusercontent.com/aptos-labs/aptos-core/097ea73b4a78c0166f22a269f27e514dc895afb4/ecosystem/typescript/sdk/LICENSE * * */ - +import { sha3_256 as sha3Hash } from "@noble/hashes/sha3"; import { - Ed25519PublicKey, - Ed25519Signature, - MultiEd25519PublicKey, - MultiEd25519Signature, - RawTransaction, - SignedTransaction, - TransactionAuthenticatorEd25519, - TransactionAuthenticatorMultiEd25519, - SigningMessage, - MultiAgentRawTransaction, - AccountAddress, - TransactionArgument, - TransactionPayload, - Identifier, - TransactionPayloadScript, - Script, - ChainId, - TransactionPayloadEntryFunction, - EntryFunction, ModuleId, + Ed25519PublicKey, + Ed25519Signature, + MultiEd25519PublicKey, + MultiEd25519Signature, + RawTransaction, + SignedTransaction, + TransactionAuthenticatorEd25519, + TransactionAuthenticatorMultiEd25519, + SigningMessage, + MultiAgentRawTransaction, + AccountAddress, + TransactionArgument, + TransactionPayload, + Identifier, + TransactionPayloadScript, + Script, + ChainId, + TransactionPayloadEntryFunction, + EntryFunction, ModuleId, } from './aptos_types'; -import { bcsToBytes, Bytes, Deserializer, Serializer, Uint64, Uint8 } from './bcs'; -import { base } from '@okxweb3/crypto-lib'; -import { HexString, MaybeHexString } from '../hex_string'; -import { ArgumentABI, EntryFunctionABI, ScriptABI, TransactionScriptABI, TypeArgumentABI } from './aptos_types/abi'; -import { argToTransactionArgument, serializeArg, TypeTagParser } from './builder_utils'; -import { EntryFunctionId, MoveFunction, MoveModuleBytecode, MoveType } from './move_types'; +import {bcsToBytes, Bytes, Deserializer, Serializer, Uint64, Uint8} from './bcs'; +import {base} from '@okxweb3/crypto-lib'; +import {HexString, MaybeHexString} from '../hex_string'; +import {ArgumentABI, EntryFunctionABI, ScriptABI, TransactionScriptABI, TypeArgumentABI} from './aptos_types/abi'; +import {argToTransactionArgument, serializeArg} from './builder_utils'; +import {EntryFunctionId, MoveFunction, MoveModuleBytecode, MoveType} from './move_types'; +import {TypeTagParser} from "./aptos_types"; +import {DEFAULT_MAX_GAS_AMOUNT, DEFAULT_TXN_EXP_SEC_FROM_NOW, MemoizeExpiring} from "../utils"; +import * as Gen from "./move_types"; const RAW_TRANSACTION_SALT = "APTOS::RawTransaction"; const RAW_TRANSACTION_WITH_DATA_SALT = "APTOS::RawTransactionWithData"; -type AnyRawTransaction = RawTransaction | MultiAgentRawTransaction; +export type AnyRawTransaction = RawTransaction | MultiAgentRawTransaction; /** * Function that takes in a Signing Message (serialized raw transaction) @@ -46,286 +49,302 @@ type AnyRawTransaction = RawTransaction | MultiAgentRawTransaction; export type SigningFn = (txn: SigningMessage) => Ed25519Signature | MultiEd25519Signature; export class TransactionBuilder { - protected readonly signingFunction: F; - - constructor(signingFunction: F) { - this.signingFunction = signingFunction; - } - - /** Generates a Signing Message out of a raw transaction. */ - static getSigningMessage(rawTxn: AnyRawTransaction): SigningMessage { - let hash : Uint8Array; - if (rawTxn instanceof RawTransaction) { - hash = base.sha3_256(Buffer.from(RAW_TRANSACTION_SALT)) - } else if (rawTxn instanceof MultiAgentRawTransaction) { - hash = base.sha3_256(Buffer.from(RAW_TRANSACTION_WITH_DATA_SALT)) - } else { - throw new Error("Unknown transaction type."); + protected readonly signingFunction: F; + + constructor(signingFunction: F, public readonly rawTxnBuilder?: TransactionBuilderABI) { + this.signingFunction = signingFunction; + } + + /** + * Builds a RawTransaction. Relays the call to TransactionBuilderABI.build + * @param func + * @param ty_tags + * @param args + */ + build(func: string, ty_tags: string[], args: any[]): RawTransaction { + if (!this.rawTxnBuilder) { + throw new Error("this.rawTxnBuilder doesn't exist."); + } + + return this.rawTxnBuilder.build(func, ty_tags, args); + } + + /** Generates a Signing Message out of a raw transaction. */ + static getSigningMessage(rawTxn: AnyRawTransaction): SigningMessage { + const hash = sha3Hash.create(); + if (rawTxn instanceof RawTransaction) { + hash.update(RAW_TRANSACTION_SALT); + } else if (rawTxn instanceof MultiAgentRawTransaction) { + hash.update(RAW_TRANSACTION_WITH_DATA_SALT); + } else { + throw new Error("Unknown transaction type."); + } + + const prefix = hash.digest(); + + const body = bcsToBytes(rawTxn); + + const mergedArray = new Uint8Array(prefix.length + body.length); + mergedArray.set(prefix); + mergedArray.set(body, prefix.length); + + return mergedArray; } - return Buffer.from([...hash, ...bcsToBytes(rawTxn)]); - } } /** * Provides signing method for signing a raw transaction with single public key. */ export class TransactionBuilderEd25519 extends TransactionBuilder { - private readonly publicKey: Uint8Array; + private readonly publicKey: Uint8Array; - constructor(signingFunction: SigningFn, publicKey: Uint8Array) { - super(signingFunction); - this.publicKey = publicKey; - } + constructor(signingFunction: SigningFn, publicKey: Uint8Array, rawTxnBuilder?: TransactionBuilderABI) { + super(signingFunction, rawTxnBuilder); + this.publicKey = publicKey; + } - private signInternal(rawTxn: RawTransaction): SignedTransaction { - const signingMessage = TransactionBuilder.getSigningMessage(rawTxn); - const signature = this.signingFunction(signingMessage); + rawToSigned(rawTxn: RawTransaction): SignedTransaction { + const signingMessage = TransactionBuilder.getSigningMessage(rawTxn); + const signature = this.signingFunction(signingMessage); - const authenticator = new TransactionAuthenticatorEd25519( - new Ed25519PublicKey(this.publicKey), - signature as Ed25519Signature, - ); + const authenticator = new TransactionAuthenticatorEd25519( + new Ed25519PublicKey(this.publicKey), + signature as Ed25519Signature, + ); - return new SignedTransaction(rawTxn, authenticator); - } + return new SignedTransaction(rawTxn, authenticator); + } - /** Signs a raw transaction and returns a bcs serialized transaction. */ - sign(rawTxn: RawTransaction): Bytes { - return bcsToBytes(this.signInternal(rawTxn)); - } + /** Signs a raw transaction and returns a bcs serialized transaction. */ + sign(rawTxn: RawTransaction): Bytes { + return bcsToBytes(this.rawToSigned(rawTxn)); + } } /** * Provides signing method for signing a raw transaction with multisig public key. */ export class TransactionBuilderMultiEd25519 extends TransactionBuilder { - private readonly publicKey: MultiEd25519PublicKey; + private readonly publicKey: MultiEd25519PublicKey; - constructor(signingFunction: SigningFn, publicKey: MultiEd25519PublicKey) { - super(signingFunction); - this.publicKey = publicKey; - } + constructor(signingFunction: SigningFn, publicKey: MultiEd25519PublicKey) { + super(signingFunction); + this.publicKey = publicKey; + } - private signInternal(rawTxn: RawTransaction): SignedTransaction { - const signingMessage = TransactionBuilder.getSigningMessage(rawTxn); - const signature = this.signingFunction(signingMessage); + rawToSigned(rawTxn: RawTransaction): SignedTransaction { + const signingMessage = TransactionBuilder.getSigningMessage(rawTxn); + const signature = this.signingFunction(signingMessage); - const authenticator = new TransactionAuthenticatorMultiEd25519(this.publicKey, signature as MultiEd25519Signature); + const authenticator = new TransactionAuthenticatorMultiEd25519(this.publicKey, signature as MultiEd25519Signature); - return new SignedTransaction(rawTxn, authenticator); - } + return new SignedTransaction(rawTxn, authenticator); + } - /** Signs a raw transaction and returns a bcs serialized transaction. */ - sign(rawTxn: RawTransaction): Bytes { - return bcsToBytes(this.signInternal(rawTxn)); - } + /** Signs a raw transaction and returns a bcs serialized transaction. */ + sign(rawTxn: RawTransaction): Bytes { + return bcsToBytes(this.rawToSigned(rawTxn)); + } } /** * Config for creating raw transactions. */ export interface ABIBuilderConfig { - sender: MaybeHexString | AccountAddress; - sequenceNumber: Uint64 | string; - gasUnitPrice: Uint64 | string; - maxGasAmount: Uint64 | string; - expTimestampSec: bigint; - chainId: Uint8 | string; + sender: MaybeHexString | AccountAddress; + sequenceNumber: Uint64 | string; + gasUnitPrice: Uint64 | string; + maxGasAmount?: Uint64 | string; + expSecFromNow?: number | string; + chainId: Uint8 | string; } /** * Builds raw transactions based on ABI */ export class TransactionBuilderABI { - private readonly abiMap: Map; + private readonly abiMap: Map; + + private readonly builderConfig: Partial; + + /** + * Constructs a TransactionBuilderABI instance + * @param abis List of binary ABIs. + * @param builderConfig Configs for creating a raw transaction. + */ + constructor(abis: Bytes[], builderConfig?: ABIBuilderConfig) { + this.abiMap = new Map(); + + abis.forEach((abi) => { + const deserializer = new Deserializer(abi); + const scriptABI = ScriptABI.deserialize(deserializer); + let k: string; + if (scriptABI instanceof EntryFunctionABI) { + const funcABI = scriptABI as EntryFunctionABI; + const {address: addr, name: moduleName} = funcABI.module_name; + k = `${HexString.fromUint8Array(addr.address).toShortString()}::${moduleName.value}::${funcABI.name}`; + } else { + const funcABI = scriptABI as TransactionScriptABI; + k = funcABI.name; + } + + if (this.abiMap.has(k)) { + throw new Error("Found conflicting ABI interfaces"); + } + + this.abiMap.set(k, scriptABI); + }); + this.builderConfig = { + maxGasAmount: BigInt(DEFAULT_MAX_GAS_AMOUNT), + expSecFromNow: DEFAULT_TXN_EXP_SEC_FROM_NOW, + ...builderConfig, + }; + } - private readonly builderConfig: ABIBuilderConfig; + private static toBCSArgs(abiArgs: any[], args: any[]): Bytes[] { + if (abiArgs.length !== args.length) { + throw new Error("Wrong number of args provided."); + } - /** - * Constructs a TransactionBuilderABI instance - * @param abis List of binary ABIs. - * @param builderConfig Configs for creating a raw transaction. - */ - constructor(abis: Bytes[], builderConfig: ABIBuilderConfig) { - this.abiMap = new Map(); + return args.map((arg, i) => { + const serializer = new Serializer(); + serializeArg(arg, abiArgs[i].type_tag, serializer); + return serializer.getBytes(); + }); + } - abis.forEach((abi) => { - const deserializer = new Deserializer(abi); - const scriptABI = ScriptABI.deserialize(deserializer); - let k: string; - if (scriptABI instanceof EntryFunctionABI) { - const funcABI = scriptABI as EntryFunctionABI; - const { address: addr, name: moduleName } = funcABI.module_name; - k = `${HexString.fromUint8Array(addr.address).toShortString()}::${ - moduleName.value - }::${funcABI.name}`; - } else { - const funcABI = scriptABI as TransactionScriptABI; - k = funcABI.name; - } - - if (this.abiMap.has(k)) { - throw new Error("Found conflicting ABI interfaces"); - } - - this.abiMap.set(k, scriptABI); - }); - this.builderConfig = builderConfig - } + private static toTransactionArguments(abiArgs: any[], args: any[]): TransactionArgument[] { + if (abiArgs.length !== args.length) { + throw new Error("Wrong number of args provided."); + } - private static toBCSArgs(abiArgs: any[], args: any[]): Bytes[] { - if (abiArgs.length !== args.length) { - throw new Error("Wrong number of args provided."); + return args.map((arg, i) => argToTransactionArgument(arg, abiArgs[i].type_tag)); } - return args.map((arg, i) => { - const serializer = new Serializer(); - serializeArg(arg, abiArgs[i].type_tag, serializer); - return serializer.getBytes(); - }); - } - - private static toTransactionArguments( - abiArgs: any[], - args: any[] - ): TransactionArgument[] { - if (abiArgs.length !== args.length) { - throw new Error("Wrong number of args provided."); + setSequenceNumber(seqNumber: Uint64 | string) { + this.builderConfig.sequenceNumber = BigInt(seqNumber); } - return args.map((arg, i) => - argToTransactionArgument(arg, abiArgs[i].type_tag) - ); - } - - setSequenceNumber(seqNumber: Uint64 | string) { - this.builderConfig.sequenceNumber = BigInt(seqNumber); - } - - /** - * Builds a TransactionPayload. For dApps, chain ID and account sequence numbers are only known to the wallet. - * Instead of building a RawTransaction (requires chainID and sequenceNumber), dApps can build a TransactionPayload - * and pass the payload to the wallet for signing and sending. - * @param func Fully qualified func names, e.g. 0x1::Coin::transfer - * @param ty_tags TypeTag strings - * @param args Function arguments - * @returns TransactionPayload - */ - buildTransactionPayload( - func: string, - ty_tags: string[], - args: any[] - ): TransactionPayload { - const typeTags = ty_tags.map((ty_arg) => - new TypeTagParser(ty_arg).parseTypeTag() - ); - - let payload: TransactionPayload; - - if (!this.abiMap.has(func)) { - throw new Error(`Cannot find function: ${func}`); + /** + * Builds a TransactionPayload. For dApps, chain ID and account sequence numbers are only known to the wallet. + * Instead of building a RawTransaction (requires chainID and sequenceNumber), dApps can build a TransactionPayload + * and pass the payload to the wallet for signing and sending. + * @param func Fully qualified func names, e.g. 0x1::aptos_account::transfer + * @param ty_tags TypeTag strings + * @param args Function arguments + * @returns TransactionPayload + */ + buildTransactionPayload(func: string, ty_tags: string[], args: any[]): TransactionPayload { + const typeTags = ty_tags.map((ty_arg) => new TypeTagParser(ty_arg).parseTypeTag()); + + let payload: TransactionPayload; + + if (!this.abiMap.has(func)) { + throw new Error(`Cannot find function: ${func}`); + } + + const scriptABI = this.abiMap.get(func); + + if (scriptABI instanceof EntryFunctionABI) { + const funcABI = scriptABI as EntryFunctionABI; + const bcsArgs = TransactionBuilderABI.toBCSArgs(funcABI.args, args); + payload = new TransactionPayloadEntryFunction( + new EntryFunction(funcABI.module_name, new Identifier(funcABI.name), typeTags, bcsArgs), + ); + } else if (scriptABI instanceof TransactionScriptABI) { + const funcABI = scriptABI as TransactionScriptABI; + const scriptArgs = TransactionBuilderABI.toTransactionArguments(funcABI.args, args); + + payload = new TransactionPayloadScript(new Script(funcABI.code, typeTags, scriptArgs)); + } else { + /* istanbul ignore next */ + throw new Error("Unknown ABI format."); + } + + return payload; } - const scriptABI = this.abiMap.get(func); - - if (scriptABI instanceof EntryFunctionABI) { - const funcABI = scriptABI as EntryFunctionABI; - const bcsArgs = TransactionBuilderABI.toBCSArgs(funcABI.args, args); - payload = new TransactionPayloadEntryFunction( - new EntryFunction( - funcABI.module_name, - new Identifier(funcABI.name), - typeTags, - bcsArgs - ) - ); - } else if (scriptABI instanceof TransactionScriptABI) { - const funcABI = scriptABI as TransactionScriptABI; - const scriptArgs = TransactionBuilderABI.toTransactionArguments( - funcABI.args, - args - ); - - payload = new TransactionPayloadScript( - new Script(funcABI.code, typeTags, scriptArgs) - ); - } else { - throw Error("unknown abi type") + /** + * Builds a RawTransaction + * @param func Fully qualified func names, e.g. 0x1::aptos_account::transfer + * @param ty_tags TypeTag strings. + * @example Below are valid value examples + * ``` + * // Structs are in format `AccountAddress::ModuleName::StructName` + * 0x1::aptos_coin::AptosCoin + * // Vectors are in format `vector` + * vector<0x1::aptos_coin::AptosCoin> + * bool + * u8 + * u16 + * u32 + * u64 + * u128 + * u256 + * address + * ``` + * @param args Function arguments + * @returns RawTransaction + */ + build(func: string, ty_tags: string[], args: any[]): RawTransaction { + const {sender, sequenceNumber, gasUnitPrice, maxGasAmount, expSecFromNow, chainId} = this.builderConfig; + + if (!gasUnitPrice) { + throw new Error("No gasUnitPrice provided."); + } + + const senderAccount = sender instanceof AccountAddress ? sender : AccountAddress.fromHex(sender!); + const expTimestampSec = BigInt(Math.floor(Date.now() / 1000) + Number(expSecFromNow)); + const payload = this.buildTransactionPayload(func, ty_tags, args); + + if (payload) { + return new RawTransaction( + senderAccount, + BigInt(sequenceNumber!), + payload, + BigInt(maxGasAmount!), + BigInt(gasUnitPrice!), + expTimestampSec, + new ChainId(Number(chainId)), + ); + } + + throw new Error("Invalid ABI."); } - return payload; - } - - /** - * Builds a RawTransaction - * @param func Fully qualified func names, e.g. 0x1::Coin::transfer - * @param ty_tags TypeTag strings. - * @example Below are valid value examples - * ``` - * // Structs are in format `AccountAddress::ModuleName::StructName` - * 0x1::aptos_coin::AptosCoin - * // Vectors are in format `vector` - * vector<0x1::aptos_coin::AptosCoin> - * bool - * u8 - * u64 - * u128 - * address - * ``` - * @param args Function arguments - * @returns RawTransaction - */ - build(func: string, ty_tags: string[], args: any[]): RawTransaction { - const { - sender, - sequenceNumber, - gasUnitPrice, - maxGasAmount, - chainId, - expTimestampSec, - } = this.builderConfig; - - const senderAccount = - sender instanceof AccountAddress - ? sender - : AccountAddress.fromHex(sender); - - const payload = this.buildTransactionPayload(func, ty_tags, args); - if (payload) { - return new RawTransaction( - senderAccount, - BigInt(sequenceNumber), - payload, - BigInt(maxGasAmount!), - BigInt(gasUnitPrice!), - expTimestampSec, - new ChainId(Number(chainId)) - ); - } - throw new Error("Invalid ABI."); - } } -export function fetchABI(modules: MoveModuleBytecode[]) { - const abis = modules - .map((module) => module.abi) - .flatMap((abi) => - abi!.exposed_functions - .filter((ef) => ef.is_entry) - .map( - (ef) => - ({ - fullName: `${abi!.address}::${abi!.name}::${ef.name}`, - ...ef, - } as MoveFunction & { fullName: string }) - ) - ); +export type RemoteABIBuilderConfig = Partial> & { + sender: MaybeHexString | AccountAddress; +}; - const abiMap = new Map(); - abis.forEach((abi) => { - abiMap.set(abi.fullName, abi); - }); +export interface AptosClientInterface { + getAccountModules: (accountAddress: MaybeHexString) => Promise; + getAccount: (accountAddress: MaybeHexString) => Promise; + getChainId: () => Promise; + estimateGasPrice: () => Promise; +} + +export function fetchABI(modules: MoveModuleBytecode[]) { + const abis = modules + .map((module) => module.abi) + .flatMap((abi) => + abi!.exposed_functions + .filter((ef) => ef.is_entry) + .map( + (ef) => + ({ + fullName: `${abi!.address}::${abi!.name}::${ef.name}`, + ...ef, + } as MoveFunction & { fullName: string }) + ) + ); + + const abiMap = new Map(); + abis.forEach((abi) => { + abiMap.set(abi.fullName, abi); + }); - return abiMap; + return abiMap; } /** @@ -339,56 +358,53 @@ export function fetchABI(modules: MoveModuleBytecode[]) { * @returns RawTransaction */ export function buildRawTransactionByABI( - modules: MoveModuleBytecode[], - builderConfig: ABIBuilderConfig, - func: EntryFunctionId, - ty_tags: MoveType[], - args: any[], + modules: MoveModuleBytecode[], + builderConfig: ABIBuilderConfig, + func: EntryFunctionId, + ty_tags: MoveType[], + args: any[], ): RawTransaction { - /* eslint no-param-reassign: ["off"] */ - const normlize = (s: string) => s.replace(/^0[xX]0*/g, '0x'); - func = normlize(func); - const funcNameParts = func.split('::'); - if (funcNameParts.length !== 3) { - throw new Error( - // eslint-disable-next-line max-len - '\'func\' needs to be a fully qualified function name in format
::::, e.g. 0x1::coins::transfer', + /* eslint no-param-reassign: ["off"] */ + const normlize = (s: string) => s.replace(/^0[xX]0*/g, "0x"); + func = normlize(func); + const funcNameParts = func.split("::"); + if (funcNameParts.length !== 3) { + throw new Error( + // eslint-disable-next-line max-len + "'func' needs to be a fully qualified function name in format
::::, e.g. 0x1::coin::transfer", + ); + } + + const [addr, module] = func.split('::'); + + // load JSON abi + const abiMap = fetchABI(modules); + if (!abiMap.has(func)) { + throw new Error(`${func} doesn't exist.`); + } + + const funcAbi = abiMap.get(func); + if (!funcAbi) { + throw Error('abi miss'); + } + + // Remove all `signer` and `&signer` from argument list because the Move VM injects those arguments. Clients do not + // need to care about those args. `signer` and `&signer` are required be in the front of the argument list. But we + // just loop through all arguments and filter out `signer` and `&signer`. + const abiArgs = funcAbi!.params.filter((param) => param !== "signer" && param !== "&signer"); + + // Convert abi string arguments to TypeArgumentABI + const typeArgABIs = abiArgs.map( + (abiArg, i) => new ArgumentABI(`var${i}`, new TypeTagParser(abiArg, ty_tags).parseTypeTag()), ); - } - - const [addr, module] = func.split('::'); - - // load JSON abi - const abiMap = fetchABI(modules); - if (!abiMap.has(func)) { - throw new Error(`${func} doesn't exist.`); - } - - const funcAbi = abiMap.get(func); - if (!funcAbi) { - throw Error('abi miss'); - } - - // Remove all `signer` and `&signer` from argument list because the Move VM injects those arguments. Clients do not - // need to care about those args. `signer` and `&signer` are required be in the front of the argument list. But we - // just loop through all arguments and filter out `signer` and `&signer`. - const originalArgs = funcAbi.params.filter( - (param) => param !== 'signer' && param !== '&signer', - ); - - // Convert string arguments to TypeArgumentABI - const typeArgABIs = originalArgs.map( - (arg, i) => - new ArgumentABI(`var${i}`, new TypeTagParser(arg).parseTypeTag()), - ); - - const entryFunctionABI = new EntryFunctionABI( - funcAbi.name, - ModuleId.fromStr(`${addr}::${module}`), - '', // Doc string - funcAbi.generic_type_params.map((_, i) => new TypeArgumentABI(`${i}`)), - typeArgABIs, - ); - const builderABI = new TransactionBuilderABI([bcsToBytes(entryFunctionABI)], builderConfig); - return builderABI.build(func, ty_tags, args); -} + + const entryFunctionABI = new EntryFunctionABI( + funcAbi!.name, + ModuleId.fromStr(`${addr}::${module}`), + "", // Doc string + funcAbi!.generic_type_params.map((_, i) => new TypeArgumentABI(`${i}`)), + typeArgABIs, + ); + const builderABI = new TransactionBuilderABI([bcsToBytes(entryFunctionABI)], builderConfig); + return builderABI.build(func, ty_tags, args); +} \ No newline at end of file diff --git a/packages/coin-aptos/src/transaction_builder/builder_utils.ts b/packages/coin-aptos/src/transaction_builder/builder_utils.ts index ca5621e..d91c880 100644 --- a/packages/coin-aptos/src/transaction_builder/builder_utils.ts +++ b/packages/coin-aptos/src/transaction_builder/builder_utils.ts @@ -11,18 +11,22 @@ import { TypeTag, TypeTagBool, TypeTagU8, + TypeTagU16, + TypeTagU32, TypeTagU64, TypeTagU128, + TypeTagU256, TypeTagAddress, AccountAddress, TypeTagVector, TypeTagStruct, - StructTag, - Identifier, TransactionArgument, TransactionArgumentBool, + TransactionArgumentU16, + TransactionArgumentU32, TransactionArgumentU64, TransactionArgumentU128, + TransactionArgumentU256, TransactionArgumentAddress, TransactionArgumentU8, TransactionArgumentU8Vector, @@ -30,300 +34,174 @@ import { import { Serializer } from "./bcs"; function assertType(val: any, types: string[] | string, message?: string) { - if (!types.includes(typeof val)) { + if (!types?.includes(typeof val)) { throw new Error( - message || `Invalid arg: ${val} type should be ${types instanceof Array ? types.join(" or ") : types}`, + message || `Invalid arg: ${val} type should be ${types instanceof Array ? types.join(" or ") : types}`, ); } } -function bail(message: string) { - throw new Error(message); -} - -function isWhiteSpace(c: string): boolean { - return !!c.match(/\s/); -} - -function isValidAlphabetic(c: string): boolean { - return !!c.match(/[_A-Za-z0-9]/g); - -} - -type TokenType = string; -type TokenValue = string; -type Token = [TokenType, TokenValue]; - -// Returns Token and Token byte size -function nextToken(tagStr: string, pos: number): [Token, number] { - const c = tagStr[pos]; - if (c === ":") { - if (tagStr.slice(pos, pos + 2) === "::") { - return [["COLON", "::"], 2]; - } - bail("Unrecognized token."); - } else if (c === "<") { - return [["LT", "<"], 1]; - } else if (c === ">") { - return [["GT", ">"], 1]; - } else if (c === ",") { - return [["COMMA", ","], 1]; - } else if (isWhiteSpace(c)) { - let res = ""; - for (let i = pos; i < tagStr.length; i += 1) { - const char = tagStr[i]; - if (isWhiteSpace(char)) { - res = `${res}${char}`; - } else { - break; - } - } - return [["SPACE", res], res.length]; - } else if (isValidAlphabetic(c)) { - let res = ""; - for (let i = pos; i < tagStr.length; i += 1) { - const char = tagStr[i]; - if (isValidAlphabetic(char)) { - res = `${res}${char}`; - } else { - break; - } - } - return [["IDENT", res], res.length]; +export function ensureBoolean(val: boolean | string): boolean { + assertType(val, ["boolean", "string"]); + if (typeof val === "boolean") { + return val; } - throw new Error("Unrecognized token."); -} -function tokenize(tagStr: string): Token[] { - let pos = 0; - const tokens = []; - while (pos < tagStr.length) { - const [token, size] = nextToken(tagStr, pos); - if (token[0] !== "SPACE") { - tokens.push(token); - } - pos += size; + if (val === "true") { + return true; + } + if (val === "false") { + return false; } - return tokens; -} -/** - * Parser to parse a type tag string - */ -export class TypeTagParser { - private readonly tokens: Token[]; + throw new Error("Invalid boolean string."); +} - constructor(tagStr: string) { - this.tokens = tokenize(tagStr); +export function ensureNumber(val: number | string): number { + assertType(val, ["number", "string"]); + if (typeof val === "number") { + return val; } - private consume(targetToken: string) { - const token = this.tokens.shift(); - if (!token || token[1] !== targetToken) { - bail("Invalid type tag."); - } + const res = Number.parseInt(val, 10); + if (Number.isNaN(res)) { + throw new Error("Invalid number string."); } - private parseCommaList(endToken: TokenValue, allowTraillingComma: boolean): TypeTag[] { - const res: TypeTag[] = []; - if (this.tokens.length <= 0) { - bail("Invalid type tag."); - } - - while (this.tokens[0][1] !== endToken) { - res.push(this.parseTypeTag()); + return res; +} - if (this.tokens.length > 0 && this.tokens[0][1] === endToken) { - break; - } +export function ensureBigInt(val: number | bigint | string): bigint { + assertType(val, ["number", "bigint", "string"]); + return BigInt(val); +} - this.consume(","); - if (this.tokens.length > 0 && this.tokens[0][1] === endToken && allowTraillingComma) { - break; - } +export function serializeArg(argVal: any, argType: TypeTag, serializer: Serializer) { + serializeArgInner(argVal, argType, serializer, 0); +} - if (this.tokens.length <= 0) { - bail("Invalid type tag."); - } - } - return res; +function serializeArgInner(argVal: any, argType: TypeTag, serializer: Serializer, depth: number) { + if (argType instanceof TypeTagBool) { + serializer.serializeBool(ensureBoolean(argVal)); + } else if (argType instanceof TypeTagU8) { + serializer.serializeU8(ensureNumber(argVal)); + } else if (argType instanceof TypeTagU16) { + serializer.serializeU16(ensureNumber(argVal)); + } else if (argType instanceof TypeTagU32) { + serializer.serializeU32(ensureNumber(argVal)); + } else if (argType instanceof TypeTagU64) { + serializer.serializeU64(ensureBigInt(argVal)); + } else if (argType instanceof TypeTagU128) { + serializer.serializeU128(ensureBigInt(argVal)); + } else if (argType instanceof TypeTagU256) { + serializer.serializeU256(ensureBigInt(argVal)); + } else if (argType instanceof TypeTagAddress) { + serializeAddress(argVal, serializer); + } else if (argType instanceof TypeTagVector) { + serializeVector(argVal, argType, serializer, depth); + } else if (argType instanceof TypeTagStruct) { + serializeStruct(argVal, argType, serializer, depth); + } else { + throw new Error("Unsupported arg type."); } +} - parseTypeTag(): TypeTag { - if (this.tokens.length === 0) { - bail("Invalid type tag."); - } - - // Pop left most element out - const [tokenTy, tokenVal] = this.tokens.shift()!; - - if (tokenVal === "u8") { - return new TypeTagU8(); - } - if (tokenVal === "u64") { - return new TypeTagU64(); - } - if (tokenVal === "u128") { - return new TypeTagU128(); - } - if (tokenVal === "bool") { - return new TypeTagBool(); - } - if (tokenVal === "address") { - return new TypeTagAddress(); - } - if (tokenVal === "vector") { - this.consume("<"); - const res = this.parseTypeTag(); - this.consume(">"); - return new TypeTagVector(res); - } - if (tokenTy === "IDENT" && (tokenVal.startsWith("0x") || tokenVal.startsWith("0X"))) { - const address = tokenVal; - this.consume("::"); - const [moduleTokenTy, module] = this.tokens.shift()!; - if (moduleTokenTy !== "IDENT") { - bail("Invalid type tag."); - } - this.consume("::"); - const [nameTokenTy, name] = this.tokens.shift()!; - if (nameTokenTy !== "IDENT") { - bail("Invalid type tag."); - } - - let tyTags: TypeTag[] = []; - // Check if the struct has ty args - if (this.tokens.length > 0 && this.tokens[0][1] === "<") { - this.consume("<"); - tyTags = this.parseCommaList(">", true); - this.consume(">"); - } - - const structTag = new StructTag( - AccountAddress.fromHex(address), - new Identifier(module), - new Identifier(name), - tyTags, - ); - return new TypeTagStruct(structTag); - } - - throw new Error("Invalid type tag."); +function serializeAddress(argVal: any, serializer: Serializer) { + let addr: AccountAddress; + if (typeof argVal === "string" || argVal instanceof HexString) { + addr = AccountAddress.fromHex(argVal); + } else if (argVal instanceof AccountAddress) { + addr = argVal; + } else { + throw new Error("Invalid account address."); } + addr.serialize(serializer); } -export function serializeArg(argVal: any, argType: TypeTag, serializer: Serializer) { - if (argType instanceof TypeTagBool) { - assertType(argVal, ["boolean", "string"]); - if (typeof argVal === "string") { - serializer.serializeBool(argVal.toLowerCase() === 'true'); - } else { - serializer.serializeBool(argVal); - } - return; - } - if (argType instanceof TypeTagU8) { - assertType(argVal, ["number", "string"]); - if (typeof argVal === "string") { - serializer.serializeU8(Number.parseInt(argVal)); - } else { - serializer.serializeU8(argVal); +function serializeVector(argVal: any, argType: TypeTagVector, serializer: Serializer, depth: number) { + // We are serializing a vector + if (argType.value instanceof TypeTagU8) { + if (argVal instanceof Uint8Array) { + serializer.serializeBytes(argVal); + return; } - return; - } - if (argType instanceof TypeTagU64) { - assertType(argVal, ["number", "bigint", "string"]); - if (typeof argVal === "string") { - serializer.serializeU64(BigInt(argVal)); - } else { - serializer.serializeU64(argVal); + if (argVal instanceof HexString) { + serializer.serializeBytes(argVal.toUint8Array()); + return; } - return; - } - if (argType instanceof TypeTagU128) { - assertType(argVal, ["number", "bigint", "string"]); if (typeof argVal === "string") { - serializer.serializeU128(BigInt(argVal)); - } else { - serializer.serializeU128(argVal); - } - return; - } - if (argType instanceof TypeTagAddress) { - let addr: AccountAddress; - if (typeof argVal === "string" || argVal instanceof HexString) { - addr = AccountAddress.fromHex(argVal); - } else if (argVal instanceof AccountAddress) { - addr = argVal; - } else { - throw new Error("Invalid account address."); + serializer.serializeStr(argVal); + return; } - addr.serialize(serializer); - return; + // If it isn't any of those types, then it must just be an actual array of numbers } - if (argType instanceof TypeTagVector) { - // We are serializing a vector - if (argType.value instanceof TypeTagU8) { - if (argVal instanceof Uint8Array) { - serializer.serializeBytes(argVal); - return; - } - if (typeof argVal === "string") { - if(argVal.startsWith("0x")) { - serializer.serializeBytes(HexString.ensure(argVal).toUint8Array()); - return; - } - serializer.serializeStr(argVal); - return; - } - } + if (!Array.isArray(argVal)) { + throw new Error("Invalid vector args."); + } - if (!(argVal instanceof Array)) { - throw new Error("Invalid vector args."); - } + serializer.serializeU32AsUleb128(argVal.length); - serializer.serializeU32AsUleb128(argVal.length); + argVal.forEach((arg) => serializeArgInner(arg, argType.value, serializer, depth + 1)); +} - argVal.forEach((arg) => serializeArg(arg, argType.value, serializer)); - return; +function serializeStruct(argVal: any, argType: TypeTag, serializer: Serializer, depth: number) { + const { address, module_name: moduleName, name, type_args: typeArgs } = (argType as TypeTagStruct).value; + const structType = `${HexString.fromUint8Array(address.address).toShortString()}::${moduleName.value}::${name.value}`; + if (structType === "0x1::string::String") { + assertType(argVal, ["string"]); + serializer.serializeStr(argVal); + } else if (structType === "0x1::object::Object") { + serializeAddress(argVal, serializer); + } else if (structType === "0x1::option::Option") { + if (typeArgs.length !== 1) { + throw new Error(`Option has the wrong number of type arguments ${typeArgs.length}`); + } + serializeOption(argVal, typeArgs[0], serializer, depth); + } else { + throw new Error("Unsupported struct type in function argument"); } - if (argType instanceof TypeTagStruct) { - const { address, module_name: moduleName, name } = (argType as TypeTagStruct).value; - if ( - `${HexString.fromUint8Array(address.address).toShortString()}::${moduleName.value}::${name.value}` !== - "0x1::string::String" - ) { - throw new Error("The only supported struct arg is of type 0x1::string::String"); - } - assertType(argVal, ["string"]); +} + +function serializeOption(argVal: any, argType: TypeTag, serializer: Serializer, depth: number) { + // For option, we determine if it's empty or not empty first + // empty option is nothing, we specifically check for undefined to prevent fuzzy matching + if (argVal === undefined || argVal === null) { + serializer.serializeU32AsUleb128(0); + } else { + // Something means we need an array of 1 + serializer.serializeU32AsUleb128(1); - serializer.serializeStr(argVal); - return; + // Serialize the inner type arg, ensuring that depth is tracked + serializeArgInner(argVal, argType, serializer, depth + 1); } - throw new Error("Unsupported arg type."); } export function argToTransactionArgument(argVal: any, argType: TypeTag): TransactionArgument { if (argType instanceof TypeTagBool) { - assertType(argVal, "boolean"); - return new TransactionArgumentBool(argVal); + return new TransactionArgumentBool(ensureBoolean(argVal)); } if (argType instanceof TypeTagU8) { - assertType(argVal, "number"); - return new TransactionArgumentU8(argVal); + return new TransactionArgumentU8(ensureNumber(argVal)); + } + if (argType instanceof TypeTagU16) { + return new TransactionArgumentU16(ensureNumber(argVal)); + } + if (argType instanceof TypeTagU32) { + return new TransactionArgumentU32(ensureNumber(argVal)); } if (argType instanceof TypeTagU64) { - assertType(argVal, ["number", "bigint"]); - return new TransactionArgumentU64(argVal); + return new TransactionArgumentU64(ensureBigInt(argVal)); } if (argType instanceof TypeTagU128) { - assertType(argVal, ["number", "bigint"]); - return new TransactionArgumentU128(argVal); + return new TransactionArgumentU128(ensureBigInt(argVal)); + } + if (argType instanceof TypeTagU256) { + return new TransactionArgumentU256(ensureBigInt(argVal)); } if (argType instanceof TypeTagAddress) { let addr: AccountAddress; - if (typeof argVal === "string") { + if (typeof argVal === "string" || argVal instanceof HexString) { addr = AccountAddress.fromHex(argVal); } else if (argVal instanceof AccountAddress) { addr = argVal; @@ -340,4 +218,4 @@ export function argToTransactionArgument(argVal: any, argType: TypeTag): Transac } throw new Error("Unknown type for TransactionArgument."); -} +} \ No newline at end of file diff --git a/packages/coin-aptos/src/transaction_builder/move_types.ts b/packages/coin-aptos/src/transaction_builder/move_types.ts index bf7e31e..2a272f2 100644 --- a/packages/coin-aptos/src/transaction_builder/move_types.ts +++ b/packages/coin-aptos/src/transaction_builder/move_types.ts @@ -61,17 +61,42 @@ export type MoveFunctionGenericTypeParam = { export type MoveFunction = { name: IdentifierWrapper; visibility: MoveFunctionVisibility; + /** + * Whether the function can be called as an entry function directly in a transaction + */ is_entry: boolean; + /** + * Whether the function is a view function or not + */ + is_view: boolean; + /** + * Generic type params associated with the Move function + */ generic_type_params: Array; + /** + * Parameters associated with the move function + */ params: Array; + /** + * Return type of the function + */ return: Array; }; export type MoveModule = { address: Address; name: IdentifierWrapper; + /** + * Friends of the module + */ friends: Array; + /** + * Public functions of the module + */ exposed_functions: Array; + /** + * Structs of the module + */ structs: Array; }; @@ -111,4 +136,26 @@ export type HexEncodedBytes = string; export type MoveModuleBytecode = { bytecode: HexEncodedBytes; abi?: MoveModule; +}; + +export type MoveStructValue = { +}; +export type U64 = string; +export type AccountData = { + sequence_number: U64; + authentication_key: HexEncodedBytes; +}; +export type GasEstimation = { + /** + * The deprioritized estimate for the gas unit price + */ + deprioritized_gas_estimate?: number; + /** + * The current estimate for the gas unit price + */ + gas_estimate: number; + /** + * The prioritized estimate for the gas unit price + */ + prioritized_gas_estimate?: number; }; \ No newline at end of file diff --git a/packages/coin-aptos/src/utils/hd-key.ts b/packages/coin-aptos/src/utils/hd-key.ts new file mode 100644 index 0000000..79d7d06 --- /dev/null +++ b/packages/coin-aptos/src/utils/hd-key.ts @@ -0,0 +1,76 @@ +import nacl from "tweetnacl"; +import { hmac } from "@noble/hashes/hmac"; +import { sha512 } from "@noble/hashes/sha512"; +import { hexToBytes } from "@noble/hashes/utils"; + +export type Keys = { + key: Uint8Array; + chainCode: Uint8Array; +}; + +const pathRegex = /^m(\/[0-9]+')+$/; + +const replaceDerive = (val: string): string => val.replace("'", ""); + +const HMAC_KEY = "ed25519 seed"; +const HARDENED_OFFSET = 0x80000000; + +export const getMasterKeyFromSeed = (seed: string): Keys => { + const h = hmac.create(sha512, HMAC_KEY); + const I = h.update(hexToBytes(seed)).digest(); + const IL = I.slice(0, 32); + const IR = I.slice(32); + return { + key: IL, + chainCode: IR, + }; +}; + +export const CKDPriv = ({ key, chainCode }: Keys, index: number): Keys => { + const buffer = new ArrayBuffer(4); + new DataView(buffer).setUint32(0, index); + const indexBytes = new Uint8Array(buffer); + const zero = new Uint8Array([0]); + const data = new Uint8Array([...zero, ...key, ...indexBytes]); + + const I = hmac.create(sha512, chainCode).update(data).digest(); + const IL = I.slice(0, 32); + const IR = I.slice(32); + return { + key: IL, + chainCode: IR, + }; +}; + +export const getPublicKey = (privateKey: Uint8Array, withZeroByte = true): Uint8Array => { + const keyPair = nacl.sign.keyPair.fromSeed(privateKey); + const signPk = keyPair.secretKey.subarray(32); + const zero = new Uint8Array([0]); + return withZeroByte ? new Uint8Array([...zero, ...signPk]) : signPk; +}; + +export const isValidPath = (path: string): boolean => { + if (!pathRegex.test(path)) { + return false; + } + return !path + .split("/") + .slice(1) + .map(replaceDerive) + .some(Number.isNaN as any); +}; + +export const derivePath = (path: string, seed: string, offset = HARDENED_OFFSET): Keys => { + if (!isValidPath(path)) { + throw new Error("Invalid derivation path"); + } + + const { key, chainCode } = getMasterKeyFromSeed(seed); + const segments = path + .split("/") + .slice(1) + .map(replaceDerive) + .map((el) => parseInt(el, 10)); + + return segments.reduce((parentKeys, segment) => CKDPriv(parentKeys, segment + offset), { key, chainCode }); +}; diff --git a/packages/coin-aptos/src/utils/index.ts b/packages/coin-aptos/src/utils/index.ts new file mode 100644 index 0000000..67cd050 --- /dev/null +++ b/packages/coin-aptos/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from "./misc"; +export * from "./memoize-decorator"; \ No newline at end of file diff --git a/packages/coin-aptos/src/utils/memoize-decorator.ts b/packages/coin-aptos/src/utils/memoize-decorator.ts new file mode 100644 index 0000000..291c878 --- /dev/null +++ b/packages/coin-aptos/src/utils/memoize-decorator.ts @@ -0,0 +1,151 @@ +/** + * Credits to https://github.com/darrylhodgins/typescript-memoize + */ + +/* eslint-disable no-param-reassign */ +/* eslint-disable no-restricted-syntax */ + +interface MemoizeArgs { + // ttl in milliseconds for cached items. After `ttlMs`, cached items are evicted automatically. If no `ttlMs` + // is provided, cached items won't get auto-evicted. + ttlMs?: number; + // produces the cache key based on `args`. + hashFunction?: boolean | ((...args: any[]) => any); + // cached items can be taged with `tags`. `tags` can be used to evict cached items + tags?: string[]; +} + +export function Memoize(args?: MemoizeArgs | MemoizeArgs["hashFunction"]) { + let hashFunction: MemoizeArgs["hashFunction"]; + let ttlMs: MemoizeArgs["ttlMs"]; + let tags: MemoizeArgs["tags"]; + + if (typeof args === "object") { + hashFunction = args.hashFunction; + ttlMs = args.ttlMs; + tags = args.tags; + } else { + hashFunction = args; + } + + return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { + if (descriptor.value != null) { + descriptor.value = getNewFunction(descriptor.value, hashFunction, ttlMs, tags); + } else if (descriptor.get != null) { + descriptor.get = getNewFunction(descriptor.get, hashFunction, ttlMs, tags); + } else { + throw new Error("Only put a Memoize() decorator on a method or get accessor."); + } + }; +} + +export function MemoizeExpiring(ttlMs: number, hashFunction?: MemoizeArgs["hashFunction"]) { + return Memoize({ + ttlMs, + hashFunction, + }); +} + +const clearCacheTagsMap: Map[]> = new Map(); + +export function clear(tags: string[]): number { + const cleared: Set> = new Set(); + for (const tag of tags) { + const maps = clearCacheTagsMap.get(tag); + if (maps) { + for (const mp of maps) { + if (!cleared.has(mp)) { + mp.clear(); + cleared.add(mp); + } + } + } + } + return cleared.size; +} + +function getNewFunction( + originalMethod: () => void, + hashFunction?: MemoizeArgs["hashFunction"], + ttlMs: number = 0, + tags?: MemoizeArgs["tags"], +) { + const propMapName = Symbol("__memoized_map__"); + + // The function returned here gets called instead of originalMethod. + // eslint-disable-next-line func-names + return function (...args: any[]) { + let returnedValue: any; + + // @ts-ignore + const that: any = this; + + // Get or create map + // eslint-disable-next-line no-prototype-builtins + if (!that.hasOwnProperty(propMapName)) { + Object.defineProperty(that, propMapName, { + configurable: false, + enumerable: false, + writable: false, + value: new Map(), + }); + } + const myMap: Map = that[propMapName]; + + if (Array.isArray(tags)) { + for (const tag of tags) { + if (clearCacheTagsMap.has(tag)) { + clearCacheTagsMap.get(tag)!.push(myMap); + } else { + clearCacheTagsMap.set(tag, [myMap]); + } + } + } + + if (hashFunction || args.length > 0 || ttlMs > 0) { + let hashKey: any; + + // If true is passed as first parameter, will automatically use every argument, passed to string + if (hashFunction === true) { + hashKey = args.map((a) => a.toString()).join("!"); + } else if (hashFunction) { + hashKey = hashFunction.apply(that, args); + } else { + // eslint-disable-next-line prefer-destructuring + hashKey = args[0]; + } + + const timestampKey = `${hashKey}__timestamp`; + let isExpired: boolean = false; + if (ttlMs > 0) { + if (!myMap.has(timestampKey)) { + // "Expired" since it was never called before + isExpired = true; + } else { + const timestamp = myMap.get(timestampKey); + isExpired = Date.now() - timestamp > ttlMs; + } + } + + if (myMap.has(hashKey) && !isExpired) { + returnedValue = myMap.get(hashKey); + } else { + returnedValue = originalMethod.apply(that, args as any); + myMap.set(hashKey, returnedValue); + if (ttlMs > 0) { + myMap.set(timestampKey, Date.now()); + } + } + } else { + const hashKey = that; + if (myMap.has(hashKey)) { + returnedValue = myMap.get(hashKey); + } else { + returnedValue = originalMethod.apply(that, args as any); + myMap.set(hashKey, returnedValue); + } + } + + return returnedValue; + }; +} diff --git a/packages/coin-aptos/src/utils/misc.ts b/packages/coin-aptos/src/utils/misc.ts new file mode 100644 index 0000000..79ac6d2 --- /dev/null +++ b/packages/coin-aptos/src/utils/misc.ts @@ -0,0 +1,6 @@ +export const DEFAULT_MAX_GAS_AMOUNT = 200000; +// Transaction expire timestamp +export const DEFAULT_TXN_EXP_SEC_FROM_NOW = 20; +// How long does SDK wait for txn to finish +export const DEFAULT_TXN_TIMEOUT_SEC = 20; +export const APTOS_COIN = "0x1::aptos_coin::AptosCoin"; \ No newline at end of file diff --git a/packages/coin-aptos/tests/aptos.test.ts b/packages/coin-aptos/tests/aptos.test.ts index 5ae1ca7..af6a1c9 100644 --- a/packages/coin-aptos/tests/aptos.test.ts +++ b/packages/coin-aptos/tests/aptos.test.ts @@ -1,9 +1,11 @@ import * as aptos from "../src" +// @ts-ignore import { base, signUtil } from '@okxweb3/crypto-lib'; +import {HexString} from "../src/hex_string" import { - AptosAccount, + AptosAccount, createRawTransaction, createRawTransactionByABI, - generateBCSTransaction, + generateBCSTransaction, offerNFTTokenPayloadObject, simulateTransaction, transfer, transferPayload, @@ -18,7 +20,9 @@ describe("aptos", () => { console.info(base.toHex(key.secretKey)) const account2 = new AptosAccount(base.fromHex("0x4a6d287353203941768551f66446d5d4a85ab685b5b444041801014ae39419b5067aec3603bdca82e52a172ec69b2505a979f1d935a59409bacae5c7f268fc26")) console.info(account2.address().hex()) + // 0x7eaead7cf02b43db13f948bc3e2704c8885b2aebf0c214ff980b791cbf227c19 console.info(account2.pubKey().hex()) + // 0x067aec3603bdca82e52a172ec69b2505a979f1d935a59409bacae5c7f268fc26 }); /* @@ -57,6 +61,7 @@ describe("aptos", () => { ) console.info(timestamp.toString()) console.info(base.toHex(data)) + // 7eaead7cf02b43db13f948bc3e2704c8885b2aebf0c214ff980b791cbf227c1906000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e736665720002204b7dca8399f8d30f496cc904787114ac7d84c97d7553128fffb0f89463c288f608e803000000000000102700000000000001000000000000000399f36200000000160020067aec3603bdca82e52a172ec69b2505a979f1d935a59409bacae5c7f268fc264041ebca08cf018a9f3e0b05327c8c4047bb56c8385f225327fd24e3c5cb54da17f49e46b2486f799cd005c30c00986f1f11bb1c66f6d9805f6fcd382af8dc490f }); test("simulate", async () => { @@ -78,6 +83,21 @@ describe("aptos", () => { BigInt(expirationTimestampSecs) ) console.info(base.toHex(t)) + // 7eaead7cf02b43db13f948bc3e2704c8885b2aebf0c214ff980b791cbf227c190b000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e736665720002200edc4410aa38b512e3173fcd1e119abb13872d6928dce0842664ad6ada1ccd2808e80300000000000010270000000000006400000000000000d077556500000000020020067aec3603bdca82e52a172ec69b2505a979f1d935a59409bacae5c7f268fc264000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + }); + + test("offerNftObject", async () => { + const account = new AptosAccount(base.fromHex("0x4a6d287353203941768551f66446d5d4a85ab685b5b444041801014ae39419b5067aec3603bdca82e52a172ec69b2505a979f1d935a59409bacae5c7f268fc26")) + const expirationTimestampSecs = Math.ceil(Date.now() / 1000) + 300 + const nftObject="0xedc4410aa38b512e3173fcd1e119abb13872d6928dce0842664ad6ada1ccd28" + const receiver ="0xedc4410aa38b512e3173fcd1e119abb13872d6928dce0842664ad6ada1ccd28" + const amount = 100000 + const payload = offerNFTTokenPayloadObject(HexString.ensure(nftObject),HexString.ensure(receiver), BigInt(amount)) + let sender = account.address() + const ts = Math.floor(Date.now()/1000) + 3000 + const rawTxn = createRawTransaction(sender, payload, BigInt(6),32, BigInt(10000), BigInt(100), BigInt(ts)) + const tx = generateBCSTransaction(account, rawTxn); + console.info(base.toHex(tx)) }); test("buildRawTransactionByABI", async () => { @@ -133,4 +153,31 @@ describe("aptos", () => { const t = generateBCSTransaction(account, rawTx) console.info(base.toHex(t)) }); + + test("buildRawTransactionByABI3", async () => { + const account = new AptosAccount(base.fromHex("0x4a6d287353203941768551f66446d5d4a85ab685b5b444041801014ae39419b5067aec3603bdca82e52a172ec69b2505a979f1d935a59409bacae5c7f268fc26")) + + //const callData = "{\"arguments\":[{\"inner\":\"0xf332c103e87eb3ef75ec1ac3d22c29e53d5c4ca5519178c3d085ac1c0a5bf061\"},{\"inner\":\"0x71f7c94805c33d32a7f9560c95f02e9d3b5bc49884a883916f03abe6da11ac08\"},\"300000\"],\"function\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::coin_listing::init_fixed_price\",\"type\":\"entry_function_payload\",\"type_arguments\":[\"0x1::aptos_coin::AptosCoin\"]}" + const callData = "{\"type\":\"entry_function_payload\",\"type_arguments\":[\"0x1::aptos_coin::AptosCoin\"],\"function\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::coin_listing::init_fixed_price\",\"arguments\":[\"0xffedd8fac12a876e1ae43272c447794af3496794046558005c56e8c07047ff1e\",\"0x71f7c94805c33d32a7f9560c95f02e9d3b5bc49884a883916f03abe6da11ac08\",200000000]}" + const moduleData = "[{\"bytecode\":\"0xa11ceb0b060000000d0100120212560368a1010489024205cb02dd0107a804a40b08cc0f800106cc100a10d610750acb1197020ce213a7050d8919140f9d190a00050106010701080109010a020b030c030b000d0600000e0600000f060000100600001106000012080000130600001406000015060000160600001706000018060000190600051a0700031c07010001071d0800041f07010000062b0700082d0800024104010601001b000100001e020100002003040108002105040108002206040108002305040108002407040108002508040108002607040108002709040108002806040108002909040108002a0a0400002c0b0c00002e0d0c00044e040e0100074f101101080750101201080451130e0100035215110108015316160002541804010603550a2301060656252600084f101101080837101201080857100201080850101201080f02100f110f1202131315171519151a151b151c151d151e151f152015211617161e161c161d161b1619161a1621161f16200f0d1216182719271a271b27120d0f160205080d010801010b0e01080f090b0e010900050503030b1001050b10010303080900060b0e010900050503030801080b0e0109000505050303030809060b0e010900080d0505030809090b0e010900080d0505050303030809050b0e010900050503080901060c010811010809010b0e010812010b1001090001080f010b0e010900010501080d01090002050b0e01090001060b0e010900010301080002070b13010900090001080201080301080401080601080701080801080a01080b01080c010805010b130109000203080d010608110405080d080d030108120c636f696e5f6c697374696e6710636f6c6c656374696f6e5f6f666665720c6665655f7363686564756c65076c697374696e670b746f6b656e5f6f66666572066576656e7473056572726f72056576656e74066f626a656374066f7074696f6e06737472696e6705746f6b656e0a636f6c6c656374696f6e0f41756374696f6e4269644576656e7412436f6c6c656374696f6e4d657461646174611c436f6c6c656374696f6e4f6666657243616e63656c65644576656e741a436f6c6c656374696f6e4f6666657246696c6c65644576656e741a436f6c6c656374696f6e4f66666572506c616365644576656e74084576656e74735631144c697374696e6743616e63656c65644576656e74124c697374696e6746696c6c65644576656e74124c697374696e67506c616365644576656e740d546f6b656e4d6574616461746117546f6b656e4f6666657243616e63656c65644576656e7415546f6b656e4f6666657246696c6c65644576656e7415546f6b656e4f66666572506c616365644576656e7406537472696e671f636f6c6c656374696f6e5f6d657461646174615f666f725f746f6b656e7631064f626a6563740a436f6c6c656374696f6e1f636f6c6c656374696f6e5f6d657461646174615f666f725f746f6b656e7632064f7074696f6e0e656d69745f6269645f6576656e741e656d69745f636f6c6c656374696f6e5f6f666665725f63616e63656c65641c656d69745f636f6c6c656374696f6e5f6f666665725f66696c6c65641c656d69745f636f6c6c656374696f6e5f6f666665725f706c6163656415656d69745f6c697374696e675f63616e63656c656413656d69745f6c697374696e675f66696c6c656413656d69745f6c697374696e675f706c6163656419656d69745f746f6b656e5f6f666665725f63616e63656c656417656d69745f746f6b656e5f6f666665725f66696c6c656417656d69745f746f6b656e5f6f666665725f706c6163656404696e697407546f6b656e49641a746f6b656e5f6d657461646174615f666f725f746f6b656e763105546f6b656e1a746f6b656e5f6d657461646174615f666f725f746f6b656e76320a6e65775f626964646572076e65775f6269640c6e65775f656e645f74696d650f70726576696f75735f6269646465720c70726576696f75735f6269641170726576696f75735f656e645f74696d650e746f6b656e5f6d657461646174610f63726561746f725f616464726573730f636f6c6c656374696f6e5f6e616d65097075726368617365720570726963651672656d61696e696e675f746f6b656e5f616d6f756e7413636f6c6c656374696f6e5f6d657461646174610673656c6c657209726f79616c746965730a636f6d6d697373696f6e0c746f6b656e5f616d6f756e741261756374696f6e5f6269645f6576656e74730b4576656e7448616e646c65156c697374696e675f706c616365645f6576656e7473176c697374696e675f63616e63656c65645f6576656e7473156c697374696e675f66696c6c65645f6576656e74731e636f6c6c656374696f6e5f6f666665725f706c616365645f6576656e747320636f6c6c656374696f6e5f6f666665725f63616e63656c65645f6576656e74731e636f6c6c656374696f6e5f6f666665725f66696c6c65645f6576656e747319746f6b656e5f6f666665725f706c616365645f6576656e74731b746f6b656e5f6f666665725f63616e63656c65645f6576656e747319746f6b656e5f6f666665725f66696c6c65645f6576656e747304747970650a746f6b656e5f6e616d651070726f70657274795f76657273696f6e046e6f6e650763726561746f72046e616d6504736f6d650e6f626a6563745f61646472657373096e6f745f666f756e640a656d69745f6576656e74106e65775f6576656e745f68616e646c65136765745f746f6b656e5f69645f6669656c647311636f6c6c656374696f6e5f6f626a656374584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000403080100000000000000126170746f733a3a6d657461646174615f7631610101000000000000000d454e4f5f4556454e54535f5631224d61726b6574706c61636520646f6573206e6f742068617665204576656e7473563101084576656e74735631010301183078313a3a6f626a6563743a3a4f626a65637447726f75700000020803052f0530033103320b100105330b1001033403350809010203360537080d0c0b10010b0e01080f0202050105380539033a033b0801030207010538053c0539033d033e033508090402050105380539033f033b080105020a400b13010800420b13010808430b13010806440b13010807450b13010804460b13010802470b13010803480b1301080c490b1301080a4a0b1301080b0602054b080d03053c0539033508090702084b080d03053c05380539033e033d033508090802054b080d03053c053903350809090206360537080d0c0b10010b0e01080f4c080d0b0b10010b0e0108124d0b1001030a02040405380539033508090b0207040538053c0539033d033e033508090c02040405380539033508090001000004050b000b0138001201020101000004080a0038010a0038020b0038031201020203000105141a0b000c0a0e0a38040c090a0929050409050c07001114270b092a050f000b010b020b030b040b050b060b070b081200380502030300010514170b000c070e0738040c060a0629050409050c07001114270b062a050f010b010b020b030b040b051202380602040300010514190b000c090e0938040c080a0829050409050c07001114270b082a050f020b010b020b030b040b050b060b071203380702050300010514170b000c070e0738040c060a0629050409050c07001114270b062a050f030b010b020b030b040b051204380802060300010514170b000c070e0738040c060a0629050409050c07001114270b062a050f040b010b020b030b040b0512063809020703000105141a0b000c0a0e0a38040c090a0929050409050c07001114270b092a050f050b010b020b030b040b050b060b070b081207380a02080300010514170b000c070e0738040c060a0629050409050c07001114270b062a050f060b010b020b030b040b051208380b02090300010514160b000c060e0638040c050a0529050409050c07001114270b052a050f070b010b020b030b04120a380c020a0300010514190b000c090e0938040c080a0829050409050c07001114270b082a050f080b010b020b030b040b050b060b07120b380d020b0300010514160b000c060e0638040c050a0529050409050c07001114270b052a050f090b010b020b030b04120c380e020c030000221a0a00380f0a0038100a0038110a0038120a0038130a0038140a0038150a0038160a0038170a00381812050c010b000b012d05020d010000240b0e0011170c010c0238000b0238190b01381a1209020e010000040e0a00381b0a00381c0a00381d38030a00381e0b00381f382012090205000505050605040502050305010508050905070000000100020003000400\",\"abi\":{\"address\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9\",\"name\":\"events\",\"friends\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::coin_listing\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer\"],\"exposed_functions\":[{\"name\":\"collection_metadata_for_tokenv1\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"address\",\"0x1::string::String\"],\"return\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::CollectionMetadata\"]},{\"name\":\"collection_metadata_for_tokenv2\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x4::collection::Collection>\"],\"return\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::CollectionMetadata\"]},{\"name\":\"emit_bid_event\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"address\",\"address\",\"u64\",\"u64\",\"0x1::option::Option
\",\"0x1::option::Option\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"],\"return\":[]},{\"name\":\"emit_collection_offer_canceled\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"address\",\"address\",\"u64\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::CollectionMetadata\"],\"return\":[]},{\"name\":\"emit_collection_offer_filled\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"address\",\"address\",\"address\",\"u64\",\"u64\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"],\"return\":[]},{\"name\":\"emit_collection_offer_placed\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"address\",\"address\",\"u64\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::CollectionMetadata\"],\"return\":[]},{\"name\":\"emit_listing_canceled\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"0x1::string::String\",\"address\",\"address\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"],\"return\":[]},{\"name\":\"emit_listing_filled\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"0x1::string::String\",\"address\",\"address\",\"address\",\"u64\",\"u64\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"],\"return\":[]},{\"name\":\"emit_listing_placed\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"0x1::string::String\",\"address\",\"address\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"],\"return\":[]},{\"name\":\"emit_token_offer_canceled\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"address\",\"address\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"],\"return\":[]},{\"name\":\"emit_token_offer_filled\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"address\",\"address\",\"address\",\"u64\",\"u64\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"],\"return\":[]},{\"name\":\"emit_token_offer_placed\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[\"key\"]}],\"params\":[\"0x1::object::Object\",\"address\",\"address\",\"u64\",\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"],\"return\":[]},{\"name\":\"init\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\"],\"return\":[]},{\"name\":\"token_metadata_for_tokenv1\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"0x3::token::TokenId\"],\"return\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"]},{\"name\":\"token_metadata_for_tokenv2\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x4::token::Token>\"],\"return\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"]}],\"structs\":[{\"name\":\"AuctionBidEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"listing\",\"type\":\"address\"},{\"name\":\"new_bidder\",\"type\":\"address\"},{\"name\":\"new_bid\",\"type\":\"u64\"},{\"name\":\"new_end_time\",\"type\":\"u64\"},{\"name\":\"previous_bidder\",\"type\":\"0x1::option::Option
\"},{\"name\":\"previous_bid\",\"type\":\"0x1::option::Option\"},{\"name\":\"previous_end_time\",\"type\":\"u64\"},{\"name\":\"token_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"}]},{\"name\":\"CollectionMetadata\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"creator_address\",\"type\":\"address\"},{\"name\":\"collection_name\",\"type\":\"0x1::string::String\"},{\"name\":\"collection\",\"type\":\"0x1::option::Option<0x1::object::Object<0x4::collection::Collection>>\"}]},{\"name\":\"CollectionOfferCanceledEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"collection_offer\",\"type\":\"address\"},{\"name\":\"purchaser\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"remaining_token_amount\",\"type\":\"u64\"},{\"name\":\"collection_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::CollectionMetadata\"}]},{\"name\":\"CollectionOfferFilledEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"collection_offer\",\"type\":\"address\"},{\"name\":\"purchaser\",\"type\":\"address\"},{\"name\":\"seller\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"royalties\",\"type\":\"u64\"},{\"name\":\"commission\",\"type\":\"u64\"},{\"name\":\"token_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"}]},{\"name\":\"CollectionOfferPlacedEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"collection_offer\",\"type\":\"address\"},{\"name\":\"purchaser\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"token_amount\",\"type\":\"u64\"},{\"name\":\"collection_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::CollectionMetadata\"}]},{\"name\":\"EventsV1\",\"is_native\":false,\"abilities\":[\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"auction_bid_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::AuctionBidEvent>\"},{\"name\":\"listing_placed_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::ListingPlacedEvent>\"},{\"name\":\"listing_canceled_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::ListingCanceledEvent>\"},{\"name\":\"listing_filled_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::ListingFilledEvent>\"},{\"name\":\"collection_offer_placed_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::CollectionOfferPlacedEvent>\"},{\"name\":\"collection_offer_canceled_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::CollectionOfferCanceledEvent>\"},{\"name\":\"collection_offer_filled_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::CollectionOfferFilledEvent>\"},{\"name\":\"token_offer_placed_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenOfferPlacedEvent>\"},{\"name\":\"token_offer_canceled_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenOfferCanceledEvent>\"},{\"name\":\"token_offer_filled_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenOfferFilledEvent>\"}]},{\"name\":\"ListingCanceledEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"type\",\"type\":\"0x1::string::String\"},{\"name\":\"listing\",\"type\":\"address\"},{\"name\":\"seller\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"token_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"}]},{\"name\":\"ListingFilledEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"type\",\"type\":\"0x1::string::String\"},{\"name\":\"listing\",\"type\":\"address\"},{\"name\":\"seller\",\"type\":\"address\"},{\"name\":\"purchaser\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"commission\",\"type\":\"u64\"},{\"name\":\"royalties\",\"type\":\"u64\"},{\"name\":\"token_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"}]},{\"name\":\"ListingPlacedEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"type\",\"type\":\"0x1::string::String\"},{\"name\":\"listing\",\"type\":\"address\"},{\"name\":\"seller\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"token_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"}]},{\"name\":\"TokenMetadata\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"creator_address\",\"type\":\"address\"},{\"name\":\"collection_name\",\"type\":\"0x1::string::String\"},{\"name\":\"collection\",\"type\":\"0x1::option::Option<0x1::object::Object<0x4::collection::Collection>>\"},{\"name\":\"token_name\",\"type\":\"0x1::string::String\"},{\"name\":\"token\",\"type\":\"0x1::option::Option<0x1::object::Object<0x4::token::Token>>\"},{\"name\":\"property_version\",\"type\":\"0x1::option::Option\"}]},{\"name\":\"TokenOfferCanceledEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"token_offer\",\"type\":\"address\"},{\"name\":\"purchaser\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"token_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"}]},{\"name\":\"TokenOfferFilledEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"token_offer\",\"type\":\"address\"},{\"name\":\"purchaser\",\"type\":\"address\"},{\"name\":\"seller\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"royalties\",\"type\":\"u64\"},{\"name\":\"commission\",\"type\":\"u64\"},{\"name\":\"token_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"}]},{\"name\":\"TokenOfferPlacedEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"token_offer\",\"type\":\"address\"},{\"name\":\"purchaser\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"token_metadata\",\"type\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"}]}]}},{\"bytecode\":\"0xa11ceb0b060000000d01001a021a480362f90104db021605f102f10207e205a40808860e800106860f4a10d00f99030ae912230c8c13e3060def19080ff71902000101020103010401050106010701080209030a0309000b000c000d0800000e0800030f070100010c1108000614070008160400031a0200031b08000b2006000323060003250600032706000830070004310701000009300b00083207000a160800001000010000120203000013040500001506070000170807000018090a000019090a00000c0b0c00001c0d0e00001d0b0f00001e0b1000001f0b010000210b11000328130101080129141400072a0a1400032b161700032c1a1b020808052d1c0100032e1d0a0108032f1e0a000833202100083421220008352301000836231400083723140002382414000a0a1a250108043927100100043a28290100093b2a0100093c2a1400093d2a1400083e2c2100083f2d2e0003401c300003413117000342311e0003433132000344311a01080845080a0003463610010801471414000348390a000349313a00034a3101000b4b2111000b4c3d11000d120d18111913181b181c261d2627330d332933113c01060b020108000105020b02010800060c02050b02010803020b020108000302050305060c050804080403010b0201080102060c080502060c0b0201080100010b02010800010b0201080303060c0b020108070b02010803020c0806010b02010807010101080801080001060b020109000103060809080a0b020108030c0b02010807050106080a010c0108070208070801010b02010900010b0201090101060c03060c0b02010900050108091e0305030503030303030303030303030306080003030305050b020108000505080c0b0d01080e080e03030106080501080f01080c0106080c020303010b0d01080e01080e01060b0d010900010b0d0109000109000106080e020805080f0405080408040303060c080f030108050408060c0809080b0108060106080601080b0108010208090805030809050805020b020109000502050b020108000508060800050c080b0106080b01080a05080806080005050b020108000208070810010b020108100c636f696e5f6c697374696e67076c697374696e67056572726f72066d6174683634066f626a656374066f7074696f6e067369676e657206737472696e670974696d657374616d7005746f6b656e07726f79616c7479066576656e74730c6665655f7363686564756c65074c697374696e6710546f6b656e5631436f6e7461696e6572064f626a6563740e6173736572745f737461727465640b4665655363686564756c6505636c6f73650f636f6d707574655f726f79616c747906537472696e67186372656174655f746f6b656e76315f636f6e7461696e657205546f6b656e236372656174655f746f6b656e76315f636f6e7461696e65725f776974685f746f6b656e1b657874726163745f6f725f7472616e736665725f746f6b656e76310f657874726163745f746f6b656e76310e436f6e7374727563746f725265660a4f626a656374436f726504696e69740d6c69737465645f6f626a6563740e6c697374696e675f6578697374730673656c6c65720d546f6b656e4d657461646174610e746f6b656e5f6d657461646174610a64656c6574655f7265660944656c6574655265660a657874656e645f72656609457874656e645265660c7472616e736665725f7265660b5472616e736665725265660e6f626a6563745f61646472657373096e6f745f666f756e640b6e6f775f7365636f6e64731d67656e65726174655f7369676e65725f666f725f657874656e64696e6707636f6e766572740a616464726573735f6f66087472616e736665720664656c65746507526f79616c7479064f7074696f6e07546f6b656e49640c6765745f746f6b656e5f69640b6765745f726f79616c7479116765745f726f79616c74795f7061796565156765745f726f79616c74795f6e756d657261746f72176765745f726f79616c74795f64656e6f6d696e61746f72036d696e0769735f736f6d650c64657374726f795f736f6d650d70617965655f61646472657373096e756d657261746f720b64656e6f6d696e61746f72136372656174655f746f6b656e5f69645f7261770e77697468647261775f746f6b656e1a6372656174655f6f626a6563745f66726f6d5f6163636f756e740f67656e65726174655f7369676e65721367656e65726174655f64656c6574655f7265661567656e65726174655f7472616e736665725f7265661b6f626a6563745f66726f6d5f636f6e7374727563746f725f7265660d6465706f7369745f746f6b656e0869735f6f776e6572117065726d697373696f6e5f64656e6965641864697361626c655f756e67617465645f7472616e736665721367656e65726174655f657874656e645f7265661c616464726573735f66726f6d5f636f6e7374727563746f725f7265661a746f6b656e5f6d657461646174615f666f725f746f6b656e76311a746f6b656e5f6d657461646174615f666f725f746f6b656e7632584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c90000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040308020000000000000003080300000000000000030804000000000000000308010000000000000005200000000000000000000000000000000000000000000000000000000000000000126170746f733a3a6d657461646174615f763184030401000000000000000b454e4f5f4c495354494e4718546865726520657869737473206e6f206c697374696e672e020000000000000014454c495354494e475f4e4f545f535441525445441c546865206c697374696e67206973206e6f7420796574206c6976652e03000000000000000c454e4f545f43524541544f521e54686520656e74697479206973206e6f74207468652063726561746f722e04000000000000000a454e4f545f4f574e45523154686520656e74697479206973206e6f7420746865206f776e6572206f6620746865207772617070656420746f6b656e2e02074c697374696e67010301183078313a3a6f626a6563743a3a4f626a65637447726f757010546f6b656e5631436f6e7461696e6572010301183078313a3a6f626a6563743a3a4f626a65637447726f7570050673656c6c65720101000c6665655f7363686564756c650101000d6c69737465645f6f626a6563740101000e746f6b656e5f6d657461646174610101000f636f6d707574655f726f79616c7479010100000205040b020108071f050c0b0201080322080924080a01020309080522080926080b000300010001110b0038000c010a0129000407050a0703110e270a012b0001110f010b0102010300020001151f0e0038002c0013000c030c020c040c070c060e0311100c050e063801290104150b010b0638021105051a0e050b060b01111238030b0211140b070b04020201000200011f8a010b000c180e1838000c170a1729000409050c0703110e270b172b000c120a12100038010c160a162901044f0b12010b162b011001111511160c1b0e1b11170c190e1b11180c130e1b11190c0f0b010b130b0f0c110c150c0a0a110600000000000000002104340600000000000000000c0705480a0a0c020b0a0b150b110c0d0c0b0c090b020b09350b0b35180b0d351a34111a0c070b070c1e0b190b1e0c060c050587010b1210001438040c1c0e1c3805047f0b1c38060c1d0e1d111e0c1a0e1d111f0c140e1d11200c100a1006000000000000000021046a0600000000000000000c0805780b010b140b100c0e0c0c350b0c35180b0e351a340c080b080c1f0b1a0b1f0c040c0305830107040600000000000000000c040c030b030b040c060c050b050b0602030300002b0f0b010b020b030b0411210c060a000b0606010000000000000011220c050b000b05110402040100002f150b0011230c020e0211240c030e0211250c040e0211260c050e030b010b040b0512012d010e023807020503000101340d0e0138082c011301010c020c030b000b0311280b021114020601040101351a0e0138080c030b010a00111238090409050e0b00010702112a270b032c011301010c020c040b0211140b000b04112802070100010037110b000c020e0238000c010a0129000409050c0703110e270b012b00100214020803000038220a0011230c030e0311260c070e07112b0e0311240c060a010a0011120b020e0311250e03112c12000c040e060b042d000e03112d0c050b000b010b0538030b060b0302090100010037110b000c020e0238000c010a0129000409050c0703110e270b012b00100014020a0100000a040e0038002900020b0100010037110b000c020e0238000c010a0129000409050c0703110e270b012b00100314020c01000200013b270b000c050e0538000c040a0429000409050c0703110e270b042b000c020a02100038010c030a032901041f0b02010b032b0110011115112e0c0105250b02100014380a112f0c010b01020000010000020001000000\",\"abi\":{\"address\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9\",\"name\":\"listing\",\"friends\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::coin_listing\"],\"exposed_functions\":[{\"name\":\"assert_started\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"],\"return\":[\"address\"]},{\"name\":\"close\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\",\"&signer\"],\"return\":[\"address\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"]},{\"name\":\"compute_royalty\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\",\"u64\"],\"return\":[\"address\",\"u64\"]},{\"name\":\"create_tokenv1_container\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"0x1::string::String\",\"u64\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::TokenV1Container>\"]},{\"name\":\"create_tokenv1_container_with_token\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"0x3::token::Token\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::TokenV1Container>\"]},{\"name\":\"extract_or_transfer_tokenv1\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::TokenV1Container>\"],\"return\":[]},{\"name\":\"extract_tokenv1\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::TokenV1Container>\"],\"return\":[]},{\"name\":\"fee_schedule\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"]},{\"name\":\"init\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"0x1::object::Object<0x1::object::ObjectCore>\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"],\"return\":[\"signer\",\"0x1::object::ConstructorRef\"]},{\"name\":\"listed_object\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"],\"return\":[\"0x1::object::Object<0x1::object::ObjectCore>\"]},{\"name\":\"listing_exists\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"],\"return\":[\"bool\"]},{\"name\":\"seller\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"],\"return\":[\"address\"]},{\"name\":\"token_metadata\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"],\"return\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::events::TokenMetadata\"]}],\"structs\":[{\"name\":\"Listing\",\"is_native\":false,\"abilities\":[\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"object\",\"type\":\"0x1::object::Object<0x1::object::ObjectCore>\"},{\"name\":\"seller\",\"type\":\"address\"},{\"name\":\"fee_schedule\",\"type\":\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"},{\"name\":\"delete_ref\",\"type\":\"0x1::object::DeleteRef\"},{\"name\":\"extend_ref\",\"type\":\"0x1::object::ExtendRef\"}]},{\"name\":\"TokenV1Container\",\"is_native\":false,\"abilities\":[\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"token\",\"type\":\"0x3::token::Token\"},{\"name\":\"delete_ref\",\"type\":\"0x1::object::DeleteRef\"},{\"name\":\"transfer_ref\",\"type\":\"0x1::object::TransferRef\"}]}]}},{\"bytecode\":\"0xa11ceb0b060000000e01001e021e580376d40204ca032c05f603af0407a508d90a08fe12800106fe133210b014ab040adb182f0b8a19020c8c19f90c0d8526120e9726020000010101020103010401050106010701080209030a0309000b000c000d000e08010001000f080000100b0000110b000412070100010d190800071a07000b1d08000521070100000e220800022704010001042a06000c2f060009360700043e0200043f0600094a0700094f0700091d04000a4f0b0000130001010000140203000015020400001602050000170206000018020500000c020700001b08020100001c08010100001e09020100001f09010100002002060000230a0b010000240a01010000250001010004300e0f010803310606000632100f0004331105010803340606000c3512130009371415000c381513000c3917010108043a190f0108013b1a010100043c1b0100083d0106000440101e0004411f2000044221010004431f220004441f1b000d452306000d46070f000147240101000248252601000949270100094b282900094c2a15000c4d17010108044e0f19010809502d2e0009510f050009522f01000553013101000e5432300004553401010805561831010009571535000958360f000959360600095a360600035b060600025c372601000d5d2306000c5e380101080b0a193b0108055f3d0501000560311801000a613e0f000a623e06000a633e06000f0d120d1716180d1918231824182816290d071809182d302f333030361838160c18183a2f3a393a3a3c3b3c02060c0b0401080100010b0401080101080201080301010103010b0401080508060c0508060806030b04010805030305060c0b040108070b04010805030304060c0b04010801080603010b08010b0401080909080c0b0a010900080b080c0b04010801050507080106080201080101060b04010900010501060c020b0401090005010b0401080701080c0405080608060301080d010805050b04010900050503080c010900010b0401090002050b0a01090001080b020b0401080105110b0a010900080e0303050b040108050b040108050308010c0c060c060c080d0503080f01080e0106080e01080f0106080f010c020b040108050303060c050302060c03010b0a01090002060c0103050806080601081002081003100b0a010900080e0303050b040108050b040108050308010c0c060c060c0503080f1c0b08010b04010809050b0a0109000b0a0109000b0a010900030b040108090b08010b04010809080b0b0401080503050b0a010900081103030305050812080d080c0b0401080105050507080107080203060c080d0301081202050812010b04010809010b0801090002060c081201080903060c0b04010900050108110106081102070b0a01090003080b04010900050505030303080c22050303050b0a0109000b0a0109000b0a01090003080b030b04010805030503050b0a0109000b0801081308130303030303050505050b04010807080c0b04010801050505070801010807010b0801081301081301060b08010900010608130b746f6b656e5f6f666665720d6170746f735f6163636f756e7404636f696e056572726f72066f626a656374066f7074696f6e067369676e657206737472696e670974696d657374616d7005746f6b656e07726f79616c7479066576656e74730c6665655f7363686564756c65076c697374696e6709436f696e4f666665720a546f6b656e4f6666657211546f6b656e4f66666572546f6b656e563111546f6b656e4f66666572546f6b656e5632064f626a6563740663616e63656c0c636f6c6c656374696f6e76310c636f6c6c656374696f6e7632096578697374735f61740f65787069726174696f6e5f74696d6507657870697265640b4665655363686564756c6506537472696e6710696e69745f666f725f746f6b656e763116696e69745f666f725f746f6b656e76315f656e74727905546f6b656e10696e69745f666f725f746f6b656e763216696e69745f666f725f746f6b656e76325f656e747279057072696365064f7074696f6e10546f6b656e5631436f6e7461696e65720c73656c6c5f746f6b656e76311273656c6c5f746f6b656e76315f656e7472790c73656c6c5f746f6b656e763205636f696e7304436f696e0a6974656d5f70726963650a64656c6574655f7265660944656c6574655265660f63726561746f725f616464726573730f636f6c6c656374696f6e5f6e616d650a746f6b656e5f6e616d651070726f70657274795f76657273696f6e0d546f6b656e4d657461646174610e6f626a6563745f61646472657373096e6f745f666f756e640a616464726573735f6f660869735f6f776e6572117065726d697373696f6e5f64656e6965641a746f6b656e5f6d657461646174615f666f725f746f6b656e763207546f6b656e4964136372656174655f746f6b656e5f69645f7261771a746f6b656e5f6d657461646174615f666f725f746f6b656e763119656d69745f746f6b656e5f6f666665725f63616e63656c6564056f776e65720d6465706f7369745f636f696e730664656c6574650b6e6f775f7365636f6e64730e436f6e7374727563746f725265660b5472616e736665725265661a6372656174655f6f626a6563745f66726f6d5f6163636f756e741567656e65726174655f7472616e736665725f7265661864697361626c655f756e67617465645f7472616e736665720f67656e65726174655f7369676e65721367656e65726174655f64656c6574655f7265660b6c697374696e675f6665650b6665655f616464726573730e7472616e736665725f636f696e73087769746864726177166f70745f696e5f6469726563745f7472616e736665720b546f6b656e446174614964146372656174655f746f6b656e5f646174615f69640f6372656174655f746f6b656e5f696417656d69745f746f6b656e5f6f666665725f706c6163656411616464726573735f746f5f6f626a65637407526f79616c74790e77697468647261775f746f6b656e136765745f6469726563745f7472616e736665721a6469726563745f6465706f7369745f776974685f6f70745f696e046e6f6e65236372656174655f746f6b656e76315f636f6e7461696e65725f776974685f746f6b656e087472616e7366657204736f6d650b6765745f726f79616c7479116765745f726f79616c74795f7061796565176765745f726f79616c74795f64656e6f6d696e61746f72156765745f726f79616c74795f6e756d657261746f720d696e76616c69645f737461746507657874726163740a636f6d6d697373696f6e17656d69745f746f6b656e5f6f666665725f66696c6c65640769735f736f6d650c64657374726f795f736f6d650d70617965655f616464726573730b64656e6f6d696e61746f72096e756d657261746f72584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c90000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040308060000000000000003080400000000000000030803000000000000000308020000000000000003080100000000000000126170746f733a3a6d657461646174615f763196040501000000000000000f454e4f5f544f4b454e5f4f46464552174e6f20746f6b656e206f6666657220646566696e65642e02000000000000000e454e4f5f434f494e5f4f46464552164e6f20636f696e206f6666657220646566696e65642e030000000000000010454e4f545f544f4b454e5f4f574e45522354686973206973206e6f7420746865206f776e6572206f662074686520746f6b656e2e04000000000000000a454e4f545f4f574e45522954686973206973206e6f7420746865206f776e6572206f662074686520746f6b656e206f666665722e06000000000000000845455850495245441c54686520746f6b656e206f666665722068617320657870697265642e0409436f696e4f66666572010301183078313a3a6f626a6563743a3a4f626a65637447726f75700a546f6b656e4f66666572010301183078313a3a6f626a6563743a3a4f626a65637447726f757011546f6b656e4f66666572546f6b656e5631010301183078313a3a6f626a6563743a3a4f626a65637447726f757011546f6b656e4f66666572546f6b656e5632010301183078313a3a6f626a6563743a3a4f626a65637447726f7570070570726963650101000765787069726564010100096578697374735f61740101000c636f6c6c656374696f6e76310101000c636f6c6c656374696f6e76320101000c6665655f7363686564756c650101000f65787069726174696f6e5f74696d65010100000201260b0a0109000102040c0b040108052803170329080b0202042b052c08062d08062e03030201090b04010807001800010404000102030c670e0138000c070a0729010407050c0b000107041110270a010a0011113801041205170b000107011113270a072a010c090a07290304240a072b0310001411140c0205360a072b020c0a0a0a1001140a0a1002140a0a1003140b0a100414111511160c020b020c050a091005140b070b0011110b091006140b0538020b010c060e0638000c080a083e003a000c030b0638030b0338040a082c0113010c040101010b04111a0a08290304600b082c030105660a08290204660b082c02010201010001020f0e0e0038000c010a0129020407050a07041110270b012b02140202010001030f0e0e0038000c010a0129010407050a07041110270b012b0314020301000001040e00380029010204010001011c110b000c010e0138000c020a0229010409050c07041110270b022b011007140205010001011c130b000c010e0138000c020a0229010409050c07041110270b022b01100714111b230206010001011c110b000c010e0138000c020a0229010409050c07041110270b022b0110051402070100001d570a000a050a060b070c0a0c0f0c0d111c0c090e09111d0c180e18111e0e09111f0c120b0d0b0f0b0a0e09112012010c100e120b102d010b120c110a000e110a050a060c170c0e0c130c140a0e0a1711210c0b0b0e11220c0c0a140b0c0b0b38050b140b1738060c080b130b0839003f000e110a010a020a030a0412022d020a000811250b010b020b0311260b0411270c150e1111110c160b050a160b0011110b060b15111638070b1638080208010400010b0b000b010b020b030b040b050b060b0738090102090100002b4a0a000a020a030b040c070c0c0c0a111c0c060e06111d0c140e14111e0e06111f0c0f0b0a0b0c0b070e06112012010c0d0e0f0b0d2d010b0f0c0e0a000e0e0a020a030c130c0b0c100c110a0b0a1311210c080b0b11220c090a110b090b0838050b110b1338060c050b100b0539003f000e0e0a0112032d030e0e11110c120b020a120b0011110b030b01111438070b123808020a01040001080b000b010b020b030b04380a01020b010001011c110b000c010e0138000c020a0229010409050c07041110270b022b01100614020c010004000102032cc6010e0138000c1b0a1b29020407050c0b000107041110270a1b2a020c1f0a1f1001140b1f1002140b020b0311150c180a000a18060100000000000000112a0c170a0138030c0f0a0f112b042a0b0f0b17112c380b0c0405350a000b17112e0c0a0a000a0a0b0f380c0b0a380d0c040b040c0b0a1811310c110b0138030b0011110b1b0e1111320e1111330e1111340b1811160c190c140c130c150c1c0c160c050a1c29010452055507041110270a1c2a010c1e111b0a1e10071423045f05640b1e0107001135270a1e1006140c0e0a1c3b00046c05710b1e0107031110270a1c3c0036000a0e380e0c060a0e0b14180b131a0c120d060a12380e0c100b150b1038040b1e1005140c0d0a0d0a0e11370c090d060a09380e0c080a0d11220b0838040a160b0638040b0d0a1c0b050b160b0e0b120b090b19380f0b1c38080c1a0e1a38000c1d0a1d3e003a000c070b1a38030b0738040a1d2c0113010c0c0101010b0c111a0a1d290304be010b1d2c030105c4010a1d290204c4010b1d2c02010b0b020d0104040001020301070b000b010b020b03381001020e0104040001020339d5010e0138000c200a2029030407050c0b000107041110270a0011110c1c0a202b031000140c1d0a1c0a1d381121041a051f0b000107021113270a0138030c100a000a1d0b1038120a1d38130c120e12381404410b00010b1238150c130e13113c0c0e0e13113d0c0b0e13113e0c0d0b0e0b0b0b0d0c040c030c0205480b0011110601000000000000000600000000000000000c040c030c020b020b030b040c170c150c190b0138030b1c0b200b190b150b170b1d11140c1e0c180c160c1a0c210c1b0c050a2129010462056507041110270a212a010c23111b0a2310071423046f05740b230107001135270a231006140c0f0a213b00047c0581010b230107031110270a213c0036000a0f380e0c060a0f0b18180b161a0c140d060a14380e0c110b1a0b1138040b231005140c0c0a0c0a0f11370c090d060a09380e0c080a0c11220b0838040a1b0b0638040b0c0a210b050b1b0b0f0b140b090b1e380f0b2138080c1f0e1f38000c220a223e003a000c070b1f38030b0738040a222c0113010c0a0101010b0a111a0a22290304ce010b222c030105d4010a22290204d4010b222c020102030002000201020202030100010101020000081800\",\"abi\":{\"address\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9\",\"name\":\"token_offer\",\"friends\":[],\"exposed_functions\":[{\"name\":\"cancel\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"],\"return\":[]},{\"name\":\"collectionv1\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"],\"return\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOfferTokenV1\"]},{\"name\":\"collectionv2\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"],\"return\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOfferTokenV2\"]},{\"name\":\"exists_at\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"],\"return\":[\"bool\"]},{\"name\":\"expiration_time\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"],\"return\":[\"u64\"]},{\"name\":\"expired\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"],\"return\":[\"bool\"]},{\"name\":\"fee_schedule\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"]},{\"name\":\"init_for_tokenv1\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"0x1::string::String\",\"u64\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\",\"u64\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"]},{\"name\":\"init_for_tokenv1_entry\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"0x1::string::String\",\"u64\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\",\"u64\"],\"return\":[]},{\"name\":\"init_for_tokenv2\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x4::token::Token>\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\",\"u64\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"]},{\"name\":\"init_for_tokenv2_entry\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x4::token::Token>\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\",\"u64\"],\"return\":[]},{\"name\":\"price\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"],\"return\":[\"u64\"]},{\"name\":\"sell_tokenv1\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\",\"0x1::string::String\",\"u64\"],\"return\":[\"0x1::option::Option<0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::TokenV1Container>>\"]},{\"name\":\"sell_tokenv1_entry\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\",\"0x1::string::String\",\"u64\"],\"return\":[]},{\"name\":\"sell_tokenv2\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::token_offer::TokenOffer>\"],\"return\":[]}],\"structs\":[{\"name\":\"CoinOffer\",\"is_native\":false,\"abilities\":[\"key\"],\"generic_type_params\":[{\"constraints\":[]}],\"fields\":[{\"name\":\"coins\",\"type\":\"0x1::coin::Coin\"}]},{\"name\":\"TokenOffer\",\"is_native\":false,\"abilities\":[\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"fee_schedule\",\"type\":\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"},{\"name\":\"item_price\",\"type\":\"u64\"},{\"name\":\"expiration_time\",\"type\":\"u64\"},{\"name\":\"delete_ref\",\"type\":\"0x1::object::DeleteRef\"}]},{\"name\":\"TokenOfferTokenV1\",\"is_native\":false,\"abilities\":[\"copy\",\"drop\",\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"creator_address\",\"type\":\"address\"},{\"name\":\"collection_name\",\"type\":\"0x1::string::String\"},{\"name\":\"token_name\",\"type\":\"0x1::string::String\"},{\"name\":\"property_version\",\"type\":\"u64\"}]},{\"name\":\"TokenOfferTokenV2\",\"is_native\":false,\"abilities\":[\"copy\",\"drop\",\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"token\",\"type\":\"0x1::object::Object<0x4::token::Token>\"}]}]}},{\"bytecode\":\"0xa11ceb0b060000000e0100180218380350f30104c3022805eb02d50307c006b30708f30d4006b30e4b10fe0e93030a9112100ba112020ca312c2070de519020ee719020000010101020103010401050106010701080009000a000b000c08010001000d0600050e070100010b0f0800051208000a13080008150700061d07010000092606000b2f080005320200023a04010001001000010100001102010100001403010100001604010100001704050100001806010100001903050100001a07010100001b08010100001c09010100001e050a0100001f0001010000200201010000210b01010000220c0101000b27050e0007280f10000b29111200032a131300052b15100108032c131300082d171800092e1a0101080b301c1b0005311e1f0208080a332110000a342213000135230101000b362425000537261e010809381a0101080639162801000b3b2a1000023c2b2c0100023d2d1301000b3e2e2f00023f302c01000140310101000a2422130004413213000942330101080b430510000b0a05210013141619001606160416181d1b161d141e1907160e1605161f13211622162416251628190b160d1602060c0b020108030002060c0a0b0201080304060c0b020108040b020108050307060c0508060806030b0201080503010b0201080307060c0508060a0806030b020108050a0304060c0a0b020108040b020108050a0307060c0a0b020108040b020108050a030a030a0b020108030a030a060c0508060a0806030b020108050a030a030a0b020108030a03010b07010303060c0b020108030303060c0a0b020108030a030605050b020108050503080801080801060c0105020b02010803060c02050b02010805010301080301060b02010900010900010a02010806010805060b0201090008060505030808010b0201080905060c0508060806030208090804010b02010900010b0201090108080a0b020108050b00010900030b020108030c0b02010804060c010b02010805020b020108050303060c050303060c0b020108040b02010805020c080a0106080a010b02010804010b0701090011030b0b0109000b0b0109000b0b010900030b02010805050b020108030303060c0b0b0109000503050808080601060b0201080302060c03010b0b01090001060b0b010900020b020108030302050302070b0b0109000302050b0b010900020303090b020109000806050505030303080804050505070b000109000c636f696e5f6c697374696e670d6170746f735f6163636f756e7404636f696e056572726f72066d6174683634066f626a656374066f7074696f6e067369676e657206737472696e67066576656e74730c6665655f7363686564756c65076c697374696e6711466978656450726963654c697374696e670d50757263686173654576656e74064f626a656374074c697374696e670f656e645f66697865645f707269636514656e645f66697865645f70726963655f6d616e790a4f626a656374436f72650b4665655363686564756c6510696e69745f66697865645f707269636506537472696e671c696e69745f66697865645f70726963655f666f725f746f6b656e763125696e69745f66697865645f70726963655f666f725f746f6b656e76315f696e7465726e616c21696e69745f66697865645f70726963655f666f725f746f6b656e76315f6d616e7919696e69745f66697865645f70726963655f696e7465726e616c15696e69745f66697865645f70726963655f6d616e79206c6973745f616e645f7570646174655f66697865645f70726963655f6d616e79286c6973745f616e645f7570646174655f66697865645f70726963655f746f6b656e76315f6d616e79064f7074696f6e0570726963650870757263686173650d70757263686173655f6d616e79127570646174655f66697865645f7072696365177570646174655f66697865645f70726963655f6d616e79097075726368617365720a636f6d6d697373696f6e09726f79616c746965730d546f6b656e4d657461646174610e746f6b656e5f6d657461646174610a616464726573735f6f6605636c6f7365117065726d697373696f6e5f64656e6965640e6f626a6563745f61646472657373096e6f745f666f756e64047574663815656d69745f6c697374696e675f63616e63656c656410546f6b656e5631436f6e7461696e6572186372656174655f746f6b656e76315f636f6e7461696e657207636f6e766572740e436f6e7374727563746f725265660b6665655f616464726573730b6c697374696e675f6665650e7472616e736665725f636f696e7304696e69741b6f626a6563745f66726f6d5f636f6e7374727563746f725f72656613656d69745f6c697374696e675f706c6163656404736f6d6504436f696e0e6173736572745f737461727465640877697468647261770576616c75650f636f6d707574655f726f79616c747907657874726163740d6465706f7369745f636f696e73036d696e13656d69745f6c697374696e675f66696c6c65640673656c6c6572584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c900000000000000000000000000000000000000000000000000000000000000010308050000000000000003080400000000000000030803000000000000000308060000000000000003080200000000000000030801000000000000000a020c0b6669786564207072696365126170746f733a3a6d657461646174615f7631fe020601000000000000000b454e4f5f4c495354494e4718546865726520657869737473206e6f206c697374696e672e02000000000000000e454e4f5f4255595f49545f4e4f57265468697320697320616e2061756374696f6e20776974686f757420627579206974206e6f772e03000000000000000c454249445f544f4f5f4c4f57215468652070726f706f7365642062696420697320696e73756666696369656e742e0400000000000000124541554354494f4e5f4e4f545f454e4445441e5468652061756374696f6e20686173206e6f742079657420656e6465642e05000000000000000e4541554354494f4e5f454e4445441e5468652061756374696f6e2068617320616c726561647920656e6465642e06000000000000000b454e4f545f53454c4c45521d54686520656e74697479206973206e6f74207468652073656c6c65722e0111466978656450726963654c697374696e67010301183078313a3a6f626a6563743a3a4f626a65637447726f7570010570726963650101000002011e0301020423051e0324032503001600010401000d2a0a01110f0c070a0011100c030a010b0011110c040c020b030a02210410051307031112270e0138000c050a053b00041a051d07051114270a053e003a000c060b04070611150b050b020b060b07380102010104010013160600000000000000000c020a020e01410523041305080a000e010a0242051438020b02060100000000000000160c0205020b0001020201040001070b000b010b020b033803010203010400010a0b000b010b020b030b040b050b0638040102040300001b0e0a000b010b020b030b0411170c070b000b0738050b050b0638030205010400131f0600000000000000000c070a070e06411323041c05080a000a010a020e030a074218140a040a050e060a074213143804010b07060100000000000000160c0705020b00010206030000202b0a000b010a020a030c070c050c0a0c0b0a0b0a0511190a050b07111a38060b0b0b0a0b05111c0c040c090a0339000c060e090b063f000e0438070c080b02070611150e0838000b0011100b030a08110f38080b080207010400131c0600000000000000000c040a040e03411323041905080a000e010a044227140a020e030a044213143803010b04060100000000000000160c0405020b0001020801040100010a0a000b010b020b0338090b000b050b06380a020901040100010d0a000b010b020b030b040b050b06380b0b000b080b09380a020a0100010010100e0038000c010a013b000407050a07051114270b013d00370014380c020b0104010029590e0111200c080a083b000407050c0b000107051114270b083e003a000c0a0a000b0a380d0c030b000b010b03070611150c120c040c090c0c0a09110f0c110e04380e0c0b0a090a0b11230c0f0c0e0a090a0c11110c070c100a0f0600000000000000002204380d040a0f380f0c0d0b0e0b0d38100a070a0b11260c060e04380e0a0611270c020d040b02380f0c050a0711190b0538100a100b0438100b070b120e0938000b100b0c11100b0b0b060b0f0b113811020c0104010013160600000000000000000c020a020e01410523041305080a000e010a0242051438120b02060100000000000000160c0205020b0001020d0104010034300a0011100c040a0111290c030b040b0321040b05100b000107031112270e0138000c050a053b000417051c0b000107051114270b053c000c060a020b063600150a01112a070611150e0138000b0011100b020b01110f3808020e01040100131a0600000000000000000c030a030e02411323041705080a000e010a034205140e020a0342131438130b03060100000000000000160c0305020b0001020000001600\",\"abi\":{\"address\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9\",\"name\":\"coin_listing\",\"friends\":[],\"exposed_functions\":[{\"name\":\"end_fixed_price\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"],\"return\":[]},{\"name\":\"end_fixed_price_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector<0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>>\"],\"return\":[]},{\"name\":\"init_fixed_price\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x1::object::ObjectCore>\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[]},{\"name\":\"init_fixed_price_for_tokenv1\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"0x1::string::String\",\"u64\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[]},{\"name\":\"init_fixed_price_for_tokenv1_internal\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"0x1::string::String\",\"u64\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"]},{\"name\":\"init_fixed_price_for_tokenv1_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"vector<0x1::string::String>\",\"u64\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"vector\"],\"return\":[]},{\"name\":\"init_fixed_price_internal\",\"visibility\":\"friend\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x1::object::ObjectCore>\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"]},{\"name\":\"init_fixed_price_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector<0x1::object::Object<0x1::object::ObjectCore>>\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"vector\"],\"return\":[]},{\"name\":\"list_and_update_fixed_price_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector<0x1::object::Object<0x1::object::ObjectCore>>\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"vector\",\"vector\",\"vector<0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>>\",\"vector\"],\"return\":[]},{\"name\":\"list_and_update_fixed_price_tokenv1_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"vector<0x1::string::String>\",\"u64\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"vector\",\"vector\",\"vector<0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>>\",\"vector\"],\"return\":[]},{\"name\":\"price\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"],\"return\":[\"0x1::option::Option\"]},{\"name\":\"purchase\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\"],\"return\":[]},{\"name\":\"purchase_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector<0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>>\"],\"return\":[]},{\"name\":\"update_fixed_price\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>\",\"u64\"],\"return\":[]},{\"name\":\"update_fixed_price_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector<0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::Listing>>\",\"vector\"],\"return\":[]}],\"structs\":[{\"name\":\"FixedPriceListing\",\"is_native\":false,\"abilities\":[\"key\"],\"generic_type_params\":[{\"constraints\":[]}],\"fields\":[{\"name\":\"price\",\"type\":\"u64\"}]},{\"name\":\"PurchaseEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"purchaser\",\"type\":\"address\"},{\"name\":\"price\",\"type\":\"u64\"},{\"name\":\"commission\",\"type\":\"u64\"},{\"name\":\"royalties\",\"type\":\"u64\"}]}]}},{\"bytecode\":\"0xa11ceb0b060000000c01001002103003409c0104dc010c05e801c60107ae03f60508a4094006e40937109b0ae4040aff0e2a0ca90fed080d961810000001010102010301040105010600070008080000090a00000a0a00000b0a00000c0600000d0a00030e07010001031d0600021f040106010521070003260200000f0001000010020300001102030000120401000013050600001407050000150701000016020300001708010000180901000019090100001a090100001b0a010003240c0601080125030300032710110003281213000329121400032a101601060714100100012b030300012c030300032d12180108042e100600032f1a1b0108013003030005311c1d0002321e01010603332014000634011d01000d0b1215160b180b1b151d2101060b0601080000020b0601080003010302060c05010b06010800010506060c050303030303060c0b060108000503060c0b060108000304060c0b06010800030301080001060b06010900030305050703030303050506080505080a0807050c080001060c01080a0106080a010807010c010804010b08010900080805080a080a0807050c0c0800010b060109000405050708000809020b06010900050101010a0201080902070b08010900090009060c0505050c0c0b0601080008090809010608070108030c6665655f7363686564756c65056572726f72056576656e74066f626a656374067369676e657206737472696e6709747970655f696e666f066576656e74730b4665655363686564756c651346697865645261746542696464696e6746656513466978656452617465436f6d6d697373696f6e134669786564526174654c697374696e674665650d4d75746174696f6e4576656e741850657263656e7461676552617465436f6d6d697373696f6e064f626a6563740d6173736572745f6578697374730b62696464696e675f6665650a636f6d6d697373696f6e05656d7074790b6665655f6164647265737304696e69740a696e69745f656e7472790b6c697374696e675f6665650f7365745f6665655f616464726573731a7365745f66697865645f726174655f62696464696e675f666565197365745f66697865645f726174655f636f6d6d697373696f6e1a7365745f66697865645f726174655f6c697374696e675f6665651e7365745f70657263656e746167655f726174655f636f6d6d697373696f6e0a657874656e645f72656609457874656e645265660f6d75746174696f6e5f6576656e74730b4576656e7448616e646c6510757064617465645f7265736f7572636506537472696e670b64656e6f6d696e61746f72096e756d657261746f720e6f626a6563745f61646472657373096e6f745f666f756e640e436f6e7374727563746f725265661a6372656174655f6f626a6563745f66726f6d5f6163636f756e741367656e65726174655f657874656e645f7265660f67656e65726174655f7369676e6572106e65775f6576656e745f68616e646c6510696e76616c69645f617267756d656e740c6f75745f6f665f72616e67651b6f626a6563745f66726f6d5f636f6e7374727563746f725f7265660a616464726573735f6f660869735f6f776e6572117065726d697373696f6e5f64656e69656404757466380a656d69745f6576656e741d67656e65726174655f7369676e65725f666f725f657874656e64696e6709747970655f6e616d65584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c90000000000000000000000000000000000000000000000000000000000000001030802000000000000000308030000000000000003080400000000000000030801000000000000000a020c0b6665655f61646472657373126170746f733a3a6d657461646174615f7631cf0404010000000000000010454e4f5f4645455f5343484544554c451b4665655363686564756c6520646f6573206e6f742065786973742e0200000000000000144544454e4f4d494e41544f525f49535f5a45524f2d5468652064656e6f6d696e61746f7220696e2061206672616374696f6e2063616e6e6f74206265207a65726f2e03000000000000001045455843454544535f4d4158494d554d3d5468652076616c756520726570726573656e7465642062792061206672616374696f6e2063616e6e6f742062652067726561746572207468616e20312e04000000000000000a454e4f545f4f574e4552395468652070617373656420696e207369676e6572206973206e6f7420746865206f776e6572206f6620746865206d61726b6574706c6163652e050b4665655363686564756c65010301183078313a3a6f626a6563743a3a4f626a65637447726f75701346697865645261746542696464696e67466565010301183078313a3a6f626a6563743a3a4f626a65637447726f757013466978656452617465436f6d6d697373696f6e010301183078313a3a6f626a6563743a3a4f626a65637447726f7570134669786564526174654c697374696e67466565010301183078313a3a6f626a6563743a3a4f626a65637447726f75701850657263656e7461676552617465436f6d6d697373696f6e010301183078313a3a6f626a6563743a3a4f626a65637447726f7570040a636f6d6d697373696f6e0101000b62696464696e675f6665650101000b6665655f616464726573730101000b6c697374696e675f66656501010000020313051c08071e0b080108040102011003020201110303020116030402012008090502022203230300010000060d0b0038000c010a0129000407050a0703110e270b01010201010001010d190e0038000c040a0429000407050a0703110e270b040c030a03290104150b032b011000140c0205170600000000000000000c020b02020201000202050e340e0038000c070a0729000407050a0703110e270b070c060a06290204150b062b021001140c0305320a062905042e0b062b050c080b010a081002140b081003140c050c04350b0435180b05351a340c0205300600000000000000000c020b020c030b0302030104000f1b0b000b010c04110f0c020e0211100c030e0211110c050b040b030e05380112000c060e050b062d000e0511130b020b050101020401000100060f0e0038000c010a0129000407050a0703110e270b012b00100414020501000017400a050a04250405050a0b000107011114270a0406000000000000000022040f05140b000107001115270b000b010c0a110f0c080e0811100c090e0811110c0c0b0a0b090e0c380112000c0d0e0c0b0d2d000e0c11130b080b0c0c0b0c070e0b0b0212012d010e0b0b0312032d030b040b0512050c060e0b0b062d050e073802020601040001090b000b010b020b030b040b051105010207010001030d190e0038000c040a0429000407050a0703110e270b040c030a03290304150b032b031005140c0205170600000000000000000c020b0202080104010019270e0138000c040a0429000407050c0b00010703110e270b040c030b010b00111738030414051707021119270b032a000c050b020a050f04150704111a0c060b050f060b0612043804020901040200011f3a0b000b010c090c030e0938000c060a062900040b05100b03010703110e270b060c050b090b03111738030418051b07021119270a052b001007111c0b050c040c080a04290104280b042c01010b080c070e070b0212012d0138050c0a0e0711170b0a0c0b2a000f060b0b12043804020a0104030002051f410b000b010c090c030e0938000c060a062900040b05100b03010703110e270b060c050b090b03111738030418051b07021119270a052b001007111c0b050c040c080a04290204290b042c0201052f0a042905042f0b042c05010b080c070e070b0212022d0238050c0a0e0711170b0a0c0b2a000f060b0b12043804020b01040200031f3a0b000b010c090c030e0938000c060a062900040b05100b03010703110e270b060c050b090b03111738030418051b07021119270a052b001007111c0b050c040c080a04290304280b042c03010b080c070e070b0212032d0338050c0a0e0711170b0a0c0b2a000f060b0b12043804020c0104030002051f560a030a02250405050a0b000107011114270a0206000000000000000022040f05140b000107001115270b000b010c0a0c040e0a38000c070a072900041f05240b04010703110e270b070c060b0a0b0411173803042c052f07021119270a062b001007111c0b060c050c090a052902043d0b052c020105430a05290504430b052c05010b090c080e080b020b0312052d0538050c0b0e0811170b0b0c0c2a000f060b0c12043804020100020005010500000003000002000100\",\"abi\":{\"address\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9\",\"name\":\"fee_schedule\",\"friends\":[],\"exposed_functions\":[{\"name\":\"assert_exists\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"],\"return\":[]},{\"name\":\"bidding_fee\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[\"u64\"]},{\"name\":\"commission\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[\"u64\"]},{\"name\":\"empty\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"address\"],\"return\":[]},{\"name\":\"fee_address\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"],\"return\":[\"address\"]},{\"name\":\"init\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"address\",\"u64\",\"u64\",\"u64\",\"u64\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"]},{\"name\":\"init_entry\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"address\",\"u64\",\"u64\",\"u64\",\"u64\"],\"return\":[]},{\"name\":\"listing_fee\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[\"u64\"]},{\"name\":\"set_fee_address\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"address\"],\"return\":[]},{\"name\":\"set_fixed_rate_bidding_fee\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[]},{\"name\":\"set_fixed_rate_commission\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[]},{\"name\":\"set_fixed_rate_listing_fee\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\"],\"return\":[]},{\"name\":\"set_percentage_rate_commission\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\",\"u64\"],\"return\":[]}],\"structs\":[{\"name\":\"FeeSchedule\",\"is_native\":false,\"abilities\":[\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"fee_address\",\"type\":\"address\"},{\"name\":\"extend_ref\",\"type\":\"0x1::object::ExtendRef\"},{\"name\":\"mutation_events\",\"type\":\"0x1::event::EventHandle<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::MutationEvent>\"}]},{\"name\":\"FixedRateBiddingFee\",\"is_native\":false,\"abilities\":[\"drop\",\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"bidding_fee\",\"type\":\"u64\"}]},{\"name\":\"FixedRateCommission\",\"is_native\":false,\"abilities\":[\"drop\",\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"commission\",\"type\":\"u64\"}]},{\"name\":\"FixedRateListingFee\",\"is_native\":false,\"abilities\":[\"drop\",\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"listing_fee\",\"type\":\"u64\"}]},{\"name\":\"MutationEvent\",\"is_native\":false,\"abilities\":[\"drop\",\"store\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"updated_resource\",\"type\":\"0x1::string::String\"}]},{\"name\":\"PercentageRateCommission\",\"is_native\":false,\"abilities\":[\"drop\",\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"denominator\",\"type\":\"u64\"},{\"name\":\"numerator\",\"type\":\"u64\"}]}]}},{\"bytecode\":\"0xa11ceb0b060000000e01002202225c037eef0204ed032e059b04b20407cd08d60b08a314800106a3153c10df15a5050a841b2c0bb01b020cb21b830f0db52a100ec52a020000010101020103010401050106010701080109020a030b030c030a000d000e000f0010080100010011080000120b0000130b000514070100010f1b0800081c07000b1f0800062407010000102508000d280800022b04010001052e06000e310600053e0200053f06000a4c07000a2804000a4d07000e4e06000c4c0b000015000101000016020300001702040000180205000019020600001a020500000e020700001d08020100001e080101000020090201000021090101000022020600002302060000260a0b010000270a01010000290c01010005320f1001080333060600073411100005351205010803360606000e371314000e381514000e3917010108053a19100108013b1a010100053c1b0100093d0106000540111e0005411f2000054221010005431f220005441f1b000f452306000f460710000147240101000248252601000a492701000e4a17010108054b101901080a4f292a000a502b2c000a511005000a522d01000653012f01001054302e000555320101080656182f01000a572a33000a583410000a593406000a5a3406000e5b2a3500035c060600025d36260100045e3706000f5f2306000260380601000e61390101080d621913010803630606000d0c193c010806643e05010006652f1801000c663f10000c673f06000c683f06000e69403500100e130e1716180e1918231824182616270e071809182c2e2e312f2e361839183a160d183b3b2e3b3d3b3e3d3f3d02060c0b0401080100010b0401080101080201080301010103010b0401080507060c0508060b0401080503030306060c0b040108070b0401080503030304060c0b04010801080603010b08010b0401080903060c0b040108010b0401080a09080d0b0b010900080d0b040108010505070801080c06080201080101060b04010900010501060c020b0401090005010b0401080701080d02050806010805060b0401090005050303080d010900010b0401090002050b0b01090001080c020b040108010511030b0b01090005080e0303050b040108050b040108050308010c0c060c060c03080f01080e0106080e01080f0106080f010c020b040108050303060c050302060c03010b0b01090002060c01250b08010b0401080903030303030305030b0b0109000b0b0109000b040108010505050708010708020b0b010900030b040108090b08010b04010809080c030b040108050303050b0b010900081003030305050811081208130405080608060301081203060c08120301081102050811010b04010809010b0801090002060c081101080903060c0b04010900050108100106081001081302070b0b0109000302030301060b0b010900080b0401090005050503030308132a05030303030303030305030b0b0109000b0b0109000b040108010505050708010708030b0b01090003080c03030b0401080503030503050b0b0109000b0801081408140303030303050505081301080a010b0801081401081401060b0801090001060814010b0401080a10636f6c6c656374696f6e5f6f666665720d6170746f735f6163636f756e7404636f696e056572726f72066d6174683634066f626a656374066f7074696f6e067369676e657206737472696e670974696d657374616d7005746f6b656e0a636f6c6c656374696f6e07726f79616c7479066576656e74730c6665655f7363686564756c65076c697374696e6709436f696e4f666665720f436f6c6c656374696f6e4f6666657216436f6c6c656374696f6e4f66666572546f6b656e563116436f6c6c656374696f6e4f66666572546f6b656e5632064f626a6563740663616e63656c0c636f6c6c656374696f6e76310c636f6c6c656374696f6e7632096578697374735f61740f65787069726174696f6e5f74696d6507657870697265640b4665655363686564756c6506537472696e6710696e69745f666f725f746f6b656e763116696e69745f666f725f746f6b656e76315f656e7472790a436f6c6c656374696f6e10696e69745f666f725f746f6b656e763216696e69745f666f725f746f6b656e76325f656e7472790570726963650972656d61696e696e67064f7074696f6e10546f6b656e5631436f6e7461696e65720c73656c6c5f746f6b656e76311273656c6c5f746f6b656e76315f656e74727905546f6b656e0c73656c6c5f746f6b656e763205636f696e7304436f696e0a6974656d5f70726963650a64656c6574655f7265660944656c6574655265660f63726561746f725f616464726573730f636f6c6c656374696f6e5f6e616d6512436f6c6c656374696f6e4d657461646174610e6f626a6563745f61646472657373096e6f745f666f756e640a616464726573735f6f660869735f6f776e6572117065726d697373696f6e5f64656e6965641f636f6c6c656374696f6e5f6d657461646174615f666f725f746f6b656e76321f636f6c6c656374696f6e5f6d657461646174615f666f725f746f6b656e76311e656d69745f636f6c6c656374696f6e5f6f666665725f63616e63656c6564056f776e65720d6465706f7369745f636f696e730664656c6574650b6e6f775f7365636f6e64730e436f6e7374727563746f725265660b5472616e736665725265661a6372656174655f6f626a6563745f66726f6d5f6163636f756e741567656e65726174655f7472616e736665725f7265661864697361626c655f756e67617465645f7472616e736665720f67656e65726174655f7369676e65721367656e65726174655f64656c6574655f7265660b6c697374696e675f6665650b6665655f616464726573730e7472616e736665725f636f696e73087769746864726177166f70745f696e5f6469726563745f7472616e736665721c656d69745f636f6c6c656374696f6e5f6f666665725f706c6163656411616464726573735f746f5f6f626a65637407526f79616c747907546f6b656e49640d546f6b656e4d65746164617461136372656174655f746f6b656e5f69645f7261770e77697468647261775f746f6b656e136765745f6469726563745f7472616e736665721a6469726563745f6465706f7369745f776974685f6f70745f696e046e6f6e65236372656174655f746f6b656e76315f636f6e7461696e65725f776974685f746f6b656e087472616e7366657204736f6d650b6765745f726f79616c7479116765745f726f79616c74795f7061796565176765745f726f79616c74795f64656e6f6d696e61746f72156765745f726f79616c74795f6e756d657261746f721a746f6b656e5f6d657461646174615f666f725f746f6b656e76310d696e76616c69645f73746174650765787472616374036d696e0a636f6d6d697373696f6e0576616c75651c656d69745f636f6c6c656374696f6e5f6f666665725f66696c6c656411636f6c6c656374696f6e5f6f626a65637410696e76616c69645f617267756d656e740769735f736f6d650c64657374726f795f736f6d650d70617965655f616464726573730b64656e6f6d696e61746f72096e756d657261746f721a746f6b656e5f6d657461646174615f666f725f746f6b656e7632584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004030806000000000000000308050000000000000003080400000000000000030802000000000000000308010000000000000003080300000000000000126170746f733a3a6d657461646174615f7631900506010000000000000014454e4f5f434f4c4c454354494f4e5f4f464645521c4e6f20636f6c6c656374696f6e206f6666657220646566696e65642e02000000000000000e454e4f5f434f494e5f4f46464552164e6f20636f696e206f6666657220646566696e65642e03000000000000000f454e4f5f544f4b454e5f4f46464552174e6f20746f6b656e206f6666657220646566696e65642e04000000000000000a454e4f545f4f574e45522e54686973206973206e6f7420746865206f776e6572206f662074686520636f6c6c656374696f6e206f666665722e05000000000000001545494e434f52524543545f434f4c4c454354494f4e38546865206f66666572656420746f6b656e206973206e6f742077697468696e2074686520657870656374656420636f6c6c656374696f6e2e06000000000000000845455850495245442154686520636f6c6c656374696f6e206f666665722068617320657870697265642e0409436f696e4f66666572010301183078313a3a6f626a6563743a3a4f626a65637447726f75700f436f6c6c656374696f6e4f66666572010301183078313a3a6f626a6563743a3a4f626a65637447726f757016436f6c6c656374696f6e4f66666572546f6b656e5631010301183078313a3a6f626a6563743a3a4f626a65637447726f757016436f6c6c656374696f6e4f66666572546f6b656e5632010301183078313a3a6f626a6563743a3a4f626a65637447726f7570080570726963650101000765787069726564010100096578697374735f61740101000972656d61696e696e670101000c636f6c6c656374696f6e76310101000c636f6c6c656374696f6e76320101000c6665655f7363686564756c650101000f65787069726174696f6e5f74696d650101000002012a0b0b0109000102050e0b040108052c03230319032d080c0202022f053008060302010b0b04010807001800010404000102030d640e0138000c060a0629010407050c0b000107041111270a010a0011123801041205170b000107021114270a062a010c080a06290304240a062b0310001411150c02052f0a062b020c0a0a0a1001140b0a10021411160c020b020c040a081003140b060b0011120a081004140b081005140b0438020b010c050e0538000c070a073e003a000c030b0538030b0338040a072c0113010c09010101010b09111a0a072903045d0b072c030105630a07290204630b072c0201020101000102100e0e0038000c010a0129020407050a07051111270b012b0214020201000103100e0e0038000c010a0129010407050a07041111270b012b0314020301000001040e00380029010204010001011c110b000c010e0138000c020a0229010409050c07041111270b022b011006140205010001011c130b000c010e0138000c020a0229010409050c07041111270b022b01100614111b230206010001011c110b000c010e0138000c020a0229010409050c07041111270b022b0110031402070100001d550a000a030a040a050b060c0b0c070c100c0e111c0c0a0e0a111d0c170e17111e0e0a111f0c130b0e0b100b070b0b0e0a112012010c110e130b112d010b130c120a000e120a030a040a05180c160c0f0c140c150a0f0a1611210c0c0b0f11220c0d0a150b0d0b0c38050b150b1638060c080b140b0839003f000e120a010a0212022d020a000811250e1211120c090b030a090b0011120b040b050b010b02111638070b0938080208010400010a0b000b010b020b030b040b050b0638090102090100001d500a000a020a030a040b050c0a0c060c0f0c0d111c0c090e09111d0c160e16111e0e09111f0c120b0d0b0f0b060b0a0e09112012010c100e120b102d010b120c110a000e110a020a030a04180c150c0e0c130c140a0e0a1511210c0b0b0e11220c0c0a140b0c0b0b38050b140b1538060c070b130b0739003f000e110a0112032d030e1111120c080b020a080b0011120b030b040b01111538070b083808020a01040001090b000b010b020b030b040b05380a01020b010001011c110b000c010e0138000c020a0229010409050c07041111270b022b01100414020c010001011c110b000c010e0138000c020a0229010409050c07041111270b022b01100514020d0100040001020328f7010e0138000c100a1029020407050c0b000107051111270a102a020c140a141001140b141002140b020b0311280c270a000a2706010000000000000011290c260a0138030c1e0a1e112a042a0b1e0b26112b380b0c0405350a000b26112d0c170a000a170b1e380c0b17380d0c040b040c180a2711300c200b0138030b0011120b100e2011310e2011320e2011330b2711340c280c230c220c240c110c250c0b0a1129010452055507041111270a112a010c13111b0a1310061423045f05640b130107001135270a131004140c1d0a113b00046c05710b130107031111270a113c0036000a1d380e0c0d0a1d0b230b220c1a0c1c0c090a1a060000000000000000210484010600000000000000000c060598010a090c050b090b1c0b1a0c0c0c0a0c070b050b07350b0a35180b0c351a3411370c060b060c210d0d0a21380e0c1f0b240b1f38040a131003140c1b0a1b0a1d11380c160a160e0d380f11370c080d0d0b08380e0c150a1b11220b1538040a250b0d38040b1b0a110b0b0b250b1d0b210b160b2838100a13100514060100000000000000170a130f05150b131005140600000000000000002104f5010b1138080c0f0e0f38000c120a123e003a000c0e0b0f38030b0e38040a122c0113010c19010101010b19111a0a12290304ef010b122c030105f5010a12290204f5010b122c02010b18020e0104040001020301070b000b010b020b03381101020f010404000102033a82020e0138000c110a1129030407050c0b000107051111270a112a030c150a0238120b15100014210417051c0b00010701113c270a0138030c200a000a020b2038130a0238140c220e223815043c0b2238160c230e2311400c1e0e2311410c190e2311420c1c0b1e0b190b1c0c050c040c0305430a0011120601000000000000000600000000000000000c050c040c030b030b040b050c270c250c290b0138030b0011120b110b290b250b270b0211430c2c0c280c260c2a0c120c2b0c0c0a122901045e056107041111270a122a010c14111b0a1410061423046b05700b140107001135270a141004140c1f0a123b000478057d0b140107031111270a123c0036000a1f380e0c0e0a1f0b280b260c1a0c1d0c0a0a1a060000000000000000210490010600000000000000000c0705a4010a0a0c060b0a0b1d0b1a0c0d0c0b0c080b060b08350b0b35180b0d351a3411370c070b070c240d0e0a24380e0c210b2a0b2138040a141003140c1b0a1b0a1f11380c170a170e0e380f11370c090d0e0b09380e0c160a1b11220b1638040a2b0b0e38040b1b0a120b0c0b2b0b1f0b240b170b2c38100a14100514060100000000000000170a140f05150b14100514060000000000000000210481020b1238080c100e1038000c130a133e003a000c0f0b1038030b0f38040a132c0113010c18010101010b18111a0a13290304fb010b132c03010581020a1329020481020b132c02010203000200020101000101010201030000071800\",\"abi\":{\"address\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9\",\"name\":\"collection_offer\",\"friends\":[],\"exposed_functions\":[{\"name\":\"cancel\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"],\"return\":[]},{\"name\":\"collectionv1\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"],\"return\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOfferTokenV1\"]},{\"name\":\"collectionv2\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"],\"return\":[\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOfferTokenV2\"]},{\"name\":\"exists_at\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"],\"return\":[\"bool\"]},{\"name\":\"expiration_time\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"],\"return\":[\"u64\"]},{\"name\":\"expired\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"],\"return\":[\"bool\"]},{\"name\":\"fee_schedule\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"]},{\"name\":\"init_for_tokenv1\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\",\"u64\",\"u64\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"]},{\"name\":\"init_for_tokenv1_entry\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\",\"u64\",\"u64\"],\"return\":[]},{\"name\":\"init_for_tokenv2\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x4::collection::Collection>\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\",\"u64\",\"u64\"],\"return\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"]},{\"name\":\"init_for_tokenv2_entry\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x4::collection::Collection>\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"u64\",\"u64\",\"u64\"],\"return\":[]},{\"name\":\"price\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"],\"return\":[\"u64\"]},{\"name\":\"remaining\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":true,\"generic_type_params\":[],\"params\":[\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\"],\"return\":[\"u64\"]},{\"name\":\"sell_tokenv1\",\"visibility\":\"public\",\"is_entry\":false,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\",\"0x1::string::String\",\"u64\"],\"return\":[\"0x1::option::Option<0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::listing::TokenV1Container>>\"]},{\"name\":\"sell_tokenv1_entry\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\",\"0x1::string::String\",\"u64\"],\"return\":[]},{\"name\":\"sell_tokenv2\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::collection_offer::CollectionOffer>\",\"0x1::object::Object<0x4::token::Token>\"],\"return\":[]}],\"structs\":[{\"name\":\"CoinOffer\",\"is_native\":false,\"abilities\":[\"key\"],\"generic_type_params\":[{\"constraints\":[]}],\"fields\":[{\"name\":\"coins\",\"type\":\"0x1::coin::Coin\"}]},{\"name\":\"CollectionOffer\",\"is_native\":false,\"abilities\":[\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"fee_schedule\",\"type\":\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\"},{\"name\":\"item_price\",\"type\":\"u64\"},{\"name\":\"remaining\",\"type\":\"u64\"},{\"name\":\"expiration_time\",\"type\":\"u64\"},{\"name\":\"delete_ref\",\"type\":\"0x1::object::DeleteRef\"}]},{\"name\":\"CollectionOfferTokenV1\",\"is_native\":false,\"abilities\":[\"copy\",\"drop\",\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"creator_address\",\"type\":\"address\"},{\"name\":\"collection_name\",\"type\":\"0x1::string::String\"}]},{\"name\":\"CollectionOfferTokenV2\",\"is_native\":false,\"abilities\":[\"copy\",\"drop\",\"key\"],\"generic_type_params\":[],\"fields\":[{\"name\":\"collection\",\"type\":\"0x1::object::Object<0x4::collection::Collection>\"}]}]}},{\"bytecode\":\"0xa11ceb0b060000000801000c020c16032248046a12057c9301078f02fd02088c05400ccc05cc0200000101010200030004000501070701000104080800020b0700050f080001120800000600010100000902010100000a03010100000c04010100000d00010100000e0501010001100709010803110b01010003130f01010003141001010003150b0101000316110101000608070a060e080a010a050a090a0a0a0b0a02060c0a050004060c0a050b000108010a0307060c0a050b000108010a030a030a050a030a060c0508020a0802030b000108010a030a030a050a0303060c0a050a0302030b000108030105010803010b0001090001090002060c0b0001080302030b00010804010301080404060c0b000108040b000108010307060c0508020a0802030b000108010a0303060c0b0001080303136d61726b6574706c6163655f73637269707473066f626a65637406737472696e670c636f696e5f6c697374696e670c6665655f7363686564756c65076c697374696e6714656e645f66697865645f70726963655f6d616e79064f626a6563740b4665655363686564756c6515696e69745f66697865645f70726963655f6d616e79206c6973745f616e645f7570646174655f66697865645f70726963655f6d616e7906537472696e67286c6973745f616e645f7570646174655f66697865645f70726963655f746f6b656e76315f6d616e790d70757263686173655f6d616e79177570646174655f66697865645f70726963655f6d616e79074c697374696e6711616464726573735f746f5f6f626a6563740f656e645f66697865645f70726963650a4f626a656374436f726510696e69745f66697865645f707269636521696e69745f66697865645f70726963655f666f725f746f6b656e76315f6d616e79087075726368617365127570646174655f66697865645f7072696365584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c900000000000000000000000000000000000000000000000000000000000000010001040006190600000000000000000c020a020e01410723041605080e010a0242071438000c030a000b0338010b02060100000000000000160c0205020b000102010104000c1e0600000000000000000c040a040e03410d23041b05080e010a0442071438020c050a000b050a020e030a04420d1438030b04060100000000000000160c0405020b00010202010400010a0a000b010b020b0338040b000b050b0638050203010400010d0a000b010b020b030b040b050b0638060b000b080b093805020401040006190600000000000000000c020a020e01410723041605080e010a0242071438000c030a000b0338070b02060100000000000000160c0205020b00010205010400061d0600000000000000000c030a030e02410d23041a05080e010a0342071438000c040a000b040e020a03420d1438080b03060100000000000000160c0305020b00010200\",\"abi\":{\"address\":\"0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9\",\"name\":\"marketplace_scripts\",\"friends\":[],\"exposed_functions\":[{\"name\":\"end_fixed_price_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector
\"],\"return\":[]},{\"name\":\"init_fixed_price_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector
\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"vector\"],\"return\":[]},{\"name\":\"list_and_update_fixed_price_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector
\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"vector\",\"vector\",\"vector
\",\"vector\"],\"return\":[]},{\"name\":\"list_and_update_fixed_price_tokenv1_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"address\",\"0x1::string::String\",\"vector<0x1::string::String>\",\"u64\",\"0x1::object::Object<0x584b50b999c78ade62f8359c91b5165ff390338d45f8e55969a04e65d76258c9::fee_schedule::FeeSchedule>\",\"vector\",\"vector\",\"vector
\",\"vector\"],\"return\":[]},{\"name\":\"purchase_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector
\"],\"return\":[]},{\"name\":\"update_fixed_price_many\",\"visibility\":\"public\",\"is_entry\":true,\"is_view\":false,\"generic_type_params\":[{\"constraints\":[]}],\"params\":[\"&signer\",\"vector
\",\"vector\"],\"return\":[]}],\"structs\":[]}}]" + const ts = Math.floor(Date.now()/1000) + 3000 + const rawTx = createRawTransactionByABI( + account.address(), + // sequenceNumber + 49n, + // chainId + 1, + // maxGasAmount + 2000n, + // gasUnitPrice + 100n, + // expirationTimestampSecs + BigInt(ts), + callData, + moduleData + ) + + const t = generateBCSTransaction(account, rawTx) + console.info(base.toHex(t)) + }); });