Skip to content

Commit

Permalink
[ts-sdk example] Adding an example for rotation offer capability and …
Browse files Browse the repository at this point in the history
…signer offer capability with signed structs (aptos-labs#9425)

* Adding the offer capability example. Uses signed structs in typescript.

* remove unnecessary aptosClient from getAccount call

Co-authored-by: Maayan <[email protected]>

* Fixing network URL unnecessary code and potential unalignment between network/faucet url

* Shortening # hyphens in output string

* Moving the chainId to the bottom of the struct list since it's potentially undefined, and explaining in the sign struct function that the proof bytes must be in that specific order.

* Cleaning up the code and making it more readable

* Formatting

* Use a much cleaner and reusable serializable class instead of a struct

---------

Co-authored-by: Maayan <[email protected]>
  • Loading branch information
2 people authored and Aalok Thakkar committed Aug 13, 2023
1 parent cdbad02 commit 8630c5c
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 0 deletions.
163 changes: 163 additions & 0 deletions ecosystem/typescript/sdk/examples/typescript-esm/offer_capabilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { AptosAccount, FaucetClient, Network, Provider, HexString, TxnBuilderTypes, BCS, Types } from "aptos";
import assert from "assert";

const ED25519_ACCOUNT_SCHEME = 0;

class SignerCapabilityOfferProofChallengeV2 {
public readonly moduleAddress: TxnBuilderTypes.AccountAddress = TxnBuilderTypes.AccountAddress.CORE_CODE_ADDRESS;
public readonly moduleName: string = "account";
public readonly structName: string = "SignerCapabilityOfferProofChallengeV2";
public readonly functionName: string = "offer_signer_capability";

constructor(
public readonly sequenceNumber: number,
public readonly sourceAddress: TxnBuilderTypes.AccountAddress,
public readonly recipientAddress: TxnBuilderTypes.AccountAddress,
) {}

serialize(serializer: BCS.Serializer): void {
this.moduleAddress.serialize(serializer);
serializer.serializeStr(this.moduleName);
serializer.serializeStr(this.structName);
serializer.serializeU64(this.sequenceNumber);
this.sourceAddress.serialize(serializer);
this.recipientAddress.serialize(serializer);
}
}

class RotationCapabilityOfferProofChallengeV2 {
public readonly moduleAddress: TxnBuilderTypes.AccountAddress = TxnBuilderTypes.AccountAddress.CORE_CODE_ADDRESS;
public readonly moduleName: string = "account";
public readonly structName: string = "RotationCapabilityOfferProofChallengeV2";
public readonly functionName: string = "offer_rotation_capability";

constructor(
public readonly chainId: number,
public readonly sequenceNumber: number,
public readonly sourceAddress: TxnBuilderTypes.AccountAddress,
public readonly recipientAddress: TxnBuilderTypes.AccountAddress,
) {}

serialize(serializer: BCS.Serializer): void {
this.moduleAddress.serialize(serializer);
serializer.serializeStr(this.moduleName);
serializer.serializeStr(this.structName);
serializer.serializeU8(this.chainId);
serializer.serializeU64(this.sequenceNumber);
this.sourceAddress.serialize(serializer);
this.recipientAddress.serialize(serializer);
}
}

const createAndFundAliceAndBob = async (
faucetClient: FaucetClient,
): Promise<{ alice: AptosAccount; bob: AptosAccount }> => {
console.log(`\n--------- Creating and funding new accounts for Bob & Alice ---------\n`);
const alice = new AptosAccount();
const bob = new AptosAccount();
await faucetClient.fundAccount(alice.address(), 100_000_000);
await faucetClient.fundAccount(bob.address(), 100_000_000);
console.log({
alice: alice.address().toString(),
bob: bob.address().toString(),
});
return {
alice,
bob,
};
};

(async () => {
const provider = new Provider(Network.DEVNET);
const faucetClient = new FaucetClient(provider.aptosClient.nodeUrl, "https://faucet.devnet.aptoslabs.com");
const chainId = await provider.getChainId();

const { alice, bob } = await createAndFundAliceAndBob(faucetClient);
const aliceAccountAddress = TxnBuilderTypes.AccountAddress.fromHex(alice.address());
const bobAccountAddress = TxnBuilderTypes.AccountAddress.fromHex(bob.address());

// Offer Alice's rotation capability to Bob
{
// Construct the RotationCapabilityOfferProofChallengeV2 struct
const rotationCapProof = new RotationCapabilityOfferProofChallengeV2(
chainId,
Number((await provider.getAccount(alice.address())).sequence_number), // Get Alice's account's latest sequence number
aliceAccountAddress,
bobAccountAddress,
);

console.log(`\n--------------- RotationCapabilityOfferProofChallengeV2 --------------\n`);

// Sign the BCS-serialized struct, submit the transaction, and wait for the result.
const res = await signStructAndSubmitTransaction(provider, alice, rotationCapProof, ED25519_ACCOUNT_SCHEME);

// Print the relevant transaction submission info
const { hash, version, success, payload } = res;
console.log("Submitted transaction results:");
console.log({ hash, version, success, payload });

// Query Alice's Account resource on-chain to verify that she has offered the rotation capability to Bob
console.log("\nChecking Alice's account resources to verify the rotation capability offer is for Bob...");
const { data } = await provider.getAccountResource(alice.address(), "0x1::account::Account");
const offerFor = (data as any).rotation_capability_offer.for.vec[0];

console.log({ rotation_capability_offer: { for: offerFor } });
assert(offerFor.toString() == bob.address().toString(), "Bob's address should be in the rotation capability offer");
console.log("...success!\n");
}

// Offer Alice's signer capability to Bob
{
// Construct the SignerCapabilityOfferProofChallengeV2 struct
const signerCapProof = new SignerCapabilityOfferProofChallengeV2(
Number((await provider.getAccount(alice.address())).sequence_number), // Get Alice's account's latest sequence number
aliceAccountAddress,
bobAccountAddress,
);

console.log(`\n--------------- SignerCapabilityOfferProofChallengeV2 ---------------\n`);

// Sign the BCS-serialized struct, submit the transaction, and wait for the result.
const res = await signStructAndSubmitTransaction(provider, alice, signerCapProof, ED25519_ACCOUNT_SCHEME);

// Print the relevant transaction submission info
const { hash, version, success, payload } = res;
console.log("Submitted transaction results:");
console.log({ hash, version, success, payload });

// Query Alice's Account resource on-chain to verify that she has offered the signer capability to Bob
console.log("\nChecking Alice's account resources to verify the signer capability offer is for Bob...");
const { data } = await provider.getAccountResource(alice.address(), "0x1::account::Account");
const offerFor = (data as any).signer_capability_offer.for.vec[0];

console.log({ signer_capability_offer: { for: offerFor } });
assert(offerFor.toString() == bob.address().toString(), "Bob's address should be in the signer capability offer\n");
console.log("...success!\n");
}
})();

const signStructAndSubmitTransaction = async (
provider: Provider,
signer: AptosAccount,
struct: SignerCapabilityOfferProofChallengeV2 | RotationCapabilityOfferProofChallengeV2,
accountScheme: number = ED25519_ACCOUNT_SCHEME,
): Promise<any> => {
const bcsStruct = BCS.bcsToBytes(struct);
const signedMessage = signer.signBuffer(bcsStruct);

const payload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural(
`${struct.moduleAddress.toHexString()}::${struct.moduleName}`,
struct.functionName,
[],
[
BCS.bcsSerializeBytes(signedMessage.toUint8Array()),
BCS.bcsSerializeU8(accountScheme),
BCS.bcsSerializeBytes(signer.pubKey().toUint8Array()),
BCS.bcsToBytes(struct.recipientAddress),
],
),
);
const txnResponse = await provider.generateSignSubmitWaitForTransaction(signer, payload);
return txnResponse as Types.UserTransaction;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"scripts": {
"build": "rm -rf dist/* && tsc -p .",
"test": "pnpm build && node ./dist/index.js",
"offer_capabilities": "ts-node --esm offer_capabilities.ts",
"rotate_key": "ts-node --esm rotate_key.ts"
},
"keywords": [],
Expand Down

0 comments on commit 8630c5c

Please sign in to comment.