forked from aptos-labs/aptos-core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ts-sdk example] Adding an example for rotation offer capability and …
…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
1 parent
cdbad02
commit 8630c5c
Showing
2 changed files
with
164 additions
and
0 deletions.
There are no files selected for viewing
163 changes: 163 additions & 0 deletions
163
ecosystem/typescript/sdk/examples/typescript-esm/offer_capabilities.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters