Skip to content

Commit 8d0fe99

Browse files
committed
deserilize arguments
1 parent 9bd4b03 commit 8d0fe99

File tree

9 files changed

+633
-35
lines changed

9 files changed

+633
-35
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* eslint-disable no-console */
2+
/* eslint-disable max-len */
3+
4+
import { Account, Aptos, AptosConfig, Network, NetworkToNetworkName, MoveVector, U8 } from "@aptos-labs/ts-sdk";
5+
6+
/**
7+
* This example demonstrate the end-to-end flow of creating, signing and submitting
8+
* a proog challenge to the Aptos chain
9+
*/
10+
11+
// Setup the client
12+
const APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK ?? Network.LOCAL];
13+
const config = new AptosConfig({ network: APTOS_NETWORK });
14+
const aptos = new Aptos(config);
15+
16+
async function main() {
17+
// Create accounts
18+
const fromAccount = Account.generate();
19+
const newAccount = Account.generate();
20+
21+
// Fund and create the accounts on chain
22+
await aptos.fundAccount({ accountAddress: fromAccount.accountAddress, amount: 1_000_000_000 });
23+
await aptos.fundAccount({ accountAddress: newAccount.accountAddress, amount: 1_000_000_000 });
24+
25+
const accountInfo = await aptos.getAccountInfo({
26+
accountAddress: fromAccount.accountAddress,
27+
});
28+
29+
// Create a rotation proof challenge.
30+
const challenge = await aptos.createProofChallenge({
31+
struct: "0x1::account::RotationProofChallenge",
32+
data: [
33+
BigInt(accountInfo.sequence_number),
34+
fromAccount.accountAddress,
35+
accountInfo.authentication_key,
36+
newAccount.publicKey.toUint8Array(),
37+
],
38+
});
39+
40+
// Display the challenge in a human readable format. This step is for
41+
// any service who needs/wants to show the challenge to the account before
42+
// they sign it.
43+
const deserializedChallenge = await aptos.getProofChallenge({
44+
struct: "0x1::account::RotationProofChallenge",
45+
data: challenge.bcsToBytes(),
46+
});
47+
48+
console.log("rotation proof challenge to sign on", deserializedChallenge);
49+
50+
// 1st account signs the challenge
51+
const proofSignedByCurrentPrivateKey = aptos.signProofChallenge({ challenge, signer: fromAccount });
52+
// 2nd account signs the challenge
53+
const proofSignedByNewPrivateKey = aptos.signProofChallenge({ challenge, signer: newAccount });
54+
55+
// Submit challenge to chain
56+
const transaction = await aptos.transaction.build.simple({
57+
sender: fromAccount.accountAddress,
58+
data: {
59+
function: "0x1::account::rotate_authentication_key",
60+
functionArguments: [
61+
new U8(fromAccount.signingScheme), // from scheme
62+
MoveVector.U8(fromAccount.publicKey.toUint8Array()),
63+
new U8(newAccount.signingScheme), // to scheme
64+
MoveVector.U8(newAccount.publicKey.toUint8Array()),
65+
MoveVector.U8(proofSignedByCurrentPrivateKey.toUint8Array()),
66+
MoveVector.U8(proofSignedByNewPrivateKey.toUint8Array()),
67+
],
68+
},
69+
});
70+
71+
const response = await aptos.signAndSubmitTransaction({ signer: fromAccount, transaction });
72+
await aptos.waitForTransaction({ transactionHash: response.hash });
73+
}
74+
75+
main();

src/api/proofChallenge.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { Account, Signature } from "../core";
5-
import { createProofChallenge, signProofChallenge } from "../internal/proofChallenge";
5+
import { createProofChallenge, getProofChallenge, signProofChallenge } from "../internal/proofChallenge";
66
import { MoveFunctionId } from "../types";
77
import { AptosConfig } from "./aptosConfig";
88
import { ProofChallenge as ProofChallengeInstance } from "../transactions/instances/proofChallenge";
@@ -36,6 +36,20 @@ export class ProofChallenge {
3636
});
3737
}
3838

39+
/**
40+
* Get the proog challenge in a human readable format
41+
*
42+
* @param args.struct The struct name
43+
* @param args.data The serialized challenge
44+
* @returns
45+
*/
46+
async getProofChallenge(args: { struct: MoveFunctionId; data: Uint8Array }) {
47+
return getProofChallenge({
48+
config: this.config,
49+
...args,
50+
});
51+
}
52+
3953
/**
4054
* Signs a generic proof challenge
4155
*

src/internal/proofChallenge.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { AptosConfig } from "../api/aptosConfig";
22
import { Account, Signature } from "../core";
3-
import { EntryFunctionArgumentTypes, SimpleEntryFunctionArgumentTypes, generateProofChallenge } from "../transactions";
3+
import {
4+
EntryFunctionArgumentTypes,
5+
SimpleEntryFunctionArgumentTypes,
6+
deserializeProofChallenge,
7+
generateProofChallenge,
8+
} from "../transactions";
49
import { ProofChallenge } from "../transactions/instances/proofChallenge";
510
import { MoveFunctionId } from "../types";
611

@@ -13,6 +18,11 @@ export async function createProofChallenge(args: {
1318
return challenge;
1419
}
1520

21+
export async function getProofChallenge(args: { config: AptosConfig; struct: MoveFunctionId; data: Uint8Array }) {
22+
const challenge = deserializeProofChallenge({ ...args });
23+
return challenge;
24+
}
25+
1626
export function signProofChallenge(args: { challenge: ProofChallenge; signer: Account }): Signature {
1727
const { challenge, signer } = args;
1828
const challengeHex = challenge.bcsToBytes();

src/transactions/transactionBuilder/helpers.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@ import {
1111
import { Bool, FixedBytes, MoveOption, MoveString, MoveVector, U128, U16, U256, U32, U64, U8 } from "../../bcs";
1212
import { AccountAddress } from "../../core";
1313
import { MoveFunction, MoveFunctionId } from "../../types";
14+
import {
15+
TypeTag,
16+
TypeTagAddress,
17+
TypeTagBool,
18+
TypeTagGeneric,
19+
TypeTagSigner,
20+
TypeTagStruct,
21+
TypeTagU128,
22+
TypeTagU16,
23+
TypeTagU256,
24+
TypeTagU32,
25+
TypeTagU64,
26+
TypeTagU8,
27+
TypeTagVector,
28+
} from "../typeTag";
1429

1530
export function isBool(arg: SimpleEntryFunctionArgumentTypes): arg is boolean {
1631
return typeof arg === "boolean";
@@ -128,3 +143,51 @@ export function getFunctionParts(functionArg: MoveFunctionId) {
128143
const functionName = funcNameParts[2];
129144
return { moduleAddress, moduleName, functionName };
130145
}
146+
147+
export function isTypeTagBool(param: TypeTag): boolean {
148+
return param instanceof TypeTagBool;
149+
}
150+
151+
export function isTypeTagAddress(param: TypeTag): boolean {
152+
return param instanceof TypeTagAddress;
153+
}
154+
155+
export function isTypeTagGeneric(param: TypeTag): boolean {
156+
return param instanceof TypeTagGeneric;
157+
}
158+
159+
export function isTypeTagSigner(param: TypeTag): boolean {
160+
return param instanceof TypeTagSigner;
161+
}
162+
163+
export function isTypeTagVector(param: TypeTag): boolean {
164+
return param instanceof TypeTagVector;
165+
}
166+
167+
export function isTypeTagStruct(param: TypeTag): boolean {
168+
return param instanceof TypeTagStruct;
169+
}
170+
171+
export function isTypeTagU8(param: TypeTag): boolean {
172+
return param instanceof TypeTagU8;
173+
}
174+
175+
export function isTypeTagU16(param: TypeTag): boolean {
176+
return param instanceof TypeTagU16;
177+
}
178+
179+
export function isTypeTagU32(param: TypeTag): boolean {
180+
return param instanceof TypeTagU32;
181+
}
182+
183+
export function isTypeTagU64(param: TypeTag): boolean {
184+
return param instanceof TypeTagU64;
185+
}
186+
187+
export function isTypeTagU128(param: TypeTag): boolean {
188+
return param instanceof TypeTagU128;
189+
}
190+
191+
export function isTypeTagU256(param: TypeTag): boolean {
192+
return param instanceof TypeTagU256;
193+
}

src/transactions/transactionBuilder/remoteAbi.ts

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import {
1111
ViewFunctionABI,
1212
FunctionABI,
1313
} from "../types";
14-
import { Bool, MoveOption, MoveString, MoveVector, U128, U16, U256, U32, U64, U8 } from "../../bcs";
15-
import { AccountAddress } from "../../core";
14+
import { Bool, Deserializer, MoveOption, MoveString, MoveVector, U128, U16, U256, U32, U64, U8 } from "../../bcs";
15+
import { AccountAddress, Ed25519PublicKey } from "../../core";
1616
import { getModule } from "../../internal/account";
1717
import {
1818
findFirstNonSignerArg,
@@ -31,9 +31,18 @@ import {
3131
isNull,
3232
isNumber,
3333
isString,
34+
isTypeTagAddress,
35+
isTypeTagBool,
36+
isTypeTagGeneric,
37+
isTypeTagU128,
38+
isTypeTagU16,
39+
isTypeTagU256,
40+
isTypeTagU32,
41+
isTypeTagU64,
42+
isTypeTagU8,
3443
throwTypeMismatch,
3544
} from "./helpers";
36-
import { MoveFunction } from "../../types";
45+
import { MoveFunction, MoveStruct } from "../../types";
3746

3847
const TEXT_ENCODER = new TextEncoder();
3948

@@ -80,7 +89,7 @@ export async function fetchStructAbi(
8089
moduleName: string,
8190
structName: string,
8291
aptosConfig: AptosConfig,
83-
) {
92+
): Promise<MoveStruct | undefined> {
8493
// This fetch from the API is currently cached
8594
const module = await getModule({ aptosConfig, accountAddress: moduleAddress, moduleName });
8695

@@ -181,7 +190,7 @@ export async function fetchStructFieldsAbi(
181190
moduleName: string,
182191
structName: string,
183192
aptosConfig: AptosConfig,
184-
) {
193+
): Promise<FunctionABI> {
185194
const structAbi = await fetchStructAbi(moduleAddress, moduleName, structName, aptosConfig);
186195

187196
// If there's no ABI, then the function is invalid
@@ -467,3 +476,107 @@ function checkType(param: TypeTag, arg: EntryFunctionArgumentTypes, position: nu
467476

468477
throw new Error(`Type mismatch for argument ${position}, expected '${param.toString()}'`);
469478
}
479+
480+
export function deserializeArgument(
481+
params: Array<TypeTag>,
482+
deserializer: Deserializer,
483+
): Array<SimpleEntryFunctionArgumentTypes> {
484+
return params.map((param) => deserializeArg(deserializer, param));
485+
}
486+
487+
export function deserializeArg(deserializer: Deserializer, param: TypeTag): SimpleEntryFunctionArgumentTypes {
488+
if (isTypeTagBool(param)) {
489+
return Bool.deserialize(deserializer).value;
490+
}
491+
if (isTypeTagAddress(param)) {
492+
return AccountAddress.deserialize(deserializer).toString();
493+
}
494+
if (isTypeTagU8(param)) {
495+
return U8.deserialize(deserializer).value;
496+
}
497+
if (isTypeTagU16(param)) {
498+
return U16.deserialize(deserializer).value;
499+
}
500+
if (isTypeTagU32(param)) {
501+
return U32.deserialize(deserializer).value;
502+
}
503+
if (isTypeTagU64(param)) {
504+
return U64.deserialize(deserializer).value;
505+
}
506+
if (isTypeTagU128(param)) {
507+
return U128.deserialize(deserializer).value;
508+
}
509+
if (isTypeTagU256(param)) {
510+
return U256.deserialize(deserializer).value;
511+
}
512+
if (isTypeTagGeneric(param)) {
513+
// // Currently, TS SDK `deserialize` can only handle a single class, not a class with generics
514+
throw new Error("Generic type deserialization is not implemented");
515+
}
516+
517+
if (param.isVector()) {
518+
if (isTypeTagU8(param.value)) {
519+
// TODO handle Secp256k1PublicKey
520+
const { values } = MoveVector.deserialize(deserializer, U8);
521+
const numbers = values.map((value) => value.value);
522+
try {
523+
return new Ed25519PublicKey(new Uint8Array(numbers)).toString();
524+
} catch (e: any) {
525+
return numbers;
526+
}
527+
}
528+
if (isTypeTagU16(param.value)) {
529+
const { values } = MoveVector.deserialize(deserializer, U16);
530+
return values.map((value) => value.value);
531+
}
532+
if (isTypeTagU32(param.value)) {
533+
const { values } = MoveVector.deserialize(deserializer, U32);
534+
return values.map((value) => value.value);
535+
}
536+
if (isTypeTagU64(param.value)) {
537+
const { values } = MoveVector.deserialize(deserializer, U64);
538+
return values.map((value) => value.value);
539+
}
540+
if (isTypeTagU128(param.value)) {
541+
const { values } = MoveVector.deserialize(deserializer, U128);
542+
return values.map((value) => value.value);
543+
}
544+
if (isTypeTagU256(param.value)) {
545+
const { values } = MoveVector.deserialize(deserializer, U256);
546+
return values.map((value) => value.value);
547+
}
548+
if (isTypeTagBool(param.value)) {
549+
const { values } = MoveVector.deserialize(deserializer, Bool);
550+
return values.map((value) => value.value);
551+
}
552+
if (isTypeTagAddress(param.value)) {
553+
const { values } = MoveVector.deserialize(deserializer, AccountAddress);
554+
return values.map((value) => value.toString());
555+
}
556+
if (param.value.isStruct()) {
557+
if (param.value.isObject()) {
558+
const { values } = MoveVector.deserialize(deserializer, AccountAddress);
559+
return values.map((value) => value.toString());
560+
}
561+
if (param.value.isOption()) {
562+
// Currently, TS SDK `deserialize` can only handle a single class, not a class with generics
563+
throw new Error("Option type deserialization is not implemented");
564+
}
565+
566+
const { values } = MoveVector.deserialize(deserializer, MoveString);
567+
return values.map((value) => value.value);
568+
}
569+
}
570+
if (param.isStruct()) {
571+
if (param.isObject()) {
572+
return AccountAddress.deserialize(deserializer).toString();
573+
}
574+
if (param.isOption()) {
575+
// Currently, TS SDK `deserialize` can only handle a single class, not a class with generics
576+
throw new Error("Option type deserialization is not implemented");
577+
}
578+
return MoveString.deserialize(deserializer).value;
579+
}
580+
581+
throw new Error(`Could not deserialize type '${param.toString()}'`);
582+
}

0 commit comments

Comments
 (0)