@aptos-labs/ts-sdk
Advanced tools
Comparing version 1.28.0 to 1.29.0
import { ChildProcessWithoutNullStreams } from 'child_process'; | ||
import { N as Network, g as AccountAddress } from '../accountAddress-OVl7-qVN.js'; | ||
import { N as Network, g as AccountAddress } from '../accountAddress-DUCC2ffJ.js'; | ||
@@ -4,0 +4,0 @@ declare class LocalNode { |
@@ -98,3 +98,3 @@ { | ||
}, | ||
"version": "1.28.0" | ||
"version": "1.29.0" | ||
} |
185
README.md
@@ -5,29 +5,19 @@ # Typescript SDK for Aptos | ||
[![Discord][discord-image]][discord-url] | ||
[![NPM Package Downloads][npm-image-downloads]][npm-url] | ||
### Reference Docs | ||
1. For SDK documentation, check out the [TypeScript SDK documentation](https://aptos.dev/sdks/new-ts-sdk/) | ||
2. For detailed reference documentation you can search, check out the [API reference documentation](https://aptos-labs.github.io/aptos-ts-sdk/) for the associated version. | ||
3. For in-depth examples, check out the [examples](./examples) folder with ready-made `package.json` files to get you going quickly! | ||
### Latest Version | ||
[![NPM Package Version][npm-image-version]][npm-url] | ||
![Node Version](https://img.shields.io/node/v/%40aptos-labs%2Fts-sdk) | ||
![NPM bundle size](https://img.shields.io/bundlephobia/min/%40aptos-labs/ts-sdk) | ||
[![NPM Package Downloads][npm-image-downloads]][npm-url] | ||
### Experimental Development Version | ||
The [TypeScript SDK](https://www.npmjs.com/package/@aptos-labs/ts-sdk) allows you to connect, explore, and interact on the Aptos blockchain. You can use it to request data, send transactions, set up test environments, and more! | ||
[![NPM Experimental Version](https://img.shields.io/npm/v/%40aptos-labs/ts-sdk/experimental)][experimental-url] | ||
![Experimental Node Version](https://img.shields.io/node/v/%40aptos-labs%2Fts-sdk/experimental) | ||
![Experimental bundle size](https://img.shields.io/bundlephobia/min/%40aptos-labs/ts-sdk/experimental) | ||
## Learn How To Use The TypeScript SDK | ||
### [Quickstart](https://aptos.dev/en/build/sdks/ts-sdk/quickstart) | ||
### [Tutorials](https://aptos.dev/en/build/sdks/ts-sdk) | ||
### [Examples](./examples) | ||
### [Reference Docs (For looking up specific functions)](https://aptos-labs.github.io/aptos-ts-sdk/) | ||
The Aptos TypeScript SDK provides a convenient way to interact with the Aptos blockchain using TypeScript. It offers a | ||
set of utility functions, classes, and types to simplify the integration process and enhance developer productivity. | ||
This repository supports version >= 0.0.0 of the [Aptos SDK npm package](https://www.npmjs.com/package/@aptos-labs/ts-sdk). | ||
## Installation | ||
##### For use in Node.js or a web application | ||
### For use in Node.js or a web application | ||
@@ -40,3 +30,3 @@ Install with your favorite package manager such as npm, yarn, or pnpm: | ||
##### For use in a browser (<= 1.9.1 version only) | ||
### For use in a browser (<= 1.9.1 version only) | ||
@@ -53,18 +43,14 @@ You can add the SDK to your web application using a script tag: | ||
Initialize `Aptos` to access the SDK API. | ||
Create an `Aptos` client in order to access the SDK's functionality. | ||
```ts | ||
// initiate the main entry point into Aptos SDK | ||
const aptos = new Aptos(); | ||
``` | ||
import {{ Aptos, AptosConfig, Network }} from "@aptos-labs/ts-sdk" | ||
If you want to pass in a custom config | ||
```ts | ||
// an optional config information for the SDK client instance. | ||
const config = new AptosConfig({ network: Network.LOCAL }); // default network is devnet | ||
// You can use AptosConfig to choose which network to connect to | ||
const config = new AptosConfig({{ network: Network.TESTNET }}); | ||
// Aptos is the main entrypoint for all functions | ||
const aptos = new Aptos(config); | ||
``` | ||
### Read data from chain | ||
### Reading Data From Onchain ([Guide](https://aptos.dev/en/build/sdks/ts-sdk/fetch-data-via-sdk)) | ||
@@ -74,3 +60,5 @@ --- | ||
```ts | ||
const fund = await aptos.getAccountInfo({ accountAddress: "0x123" }); | ||
const modules = await aptos.getAccountModules({ accountAddress: "0x123" }); | ||
const tokens = await aptos.getAccountOwnedTokens({ accountAddress: "0x123" }); | ||
``` | ||
@@ -80,3 +68,3 @@ | ||
> Note: We introduce a Single Sender authentication (as introduced in [AIP-55](https://github.com/aptos-foundation/AIPs/pull/263)). Generating an account defaults to Legacy Ed25519 authentication with the option to use the Single Sender unified authentication | ||
> Note: We introduce a Single Sender authentication (as introduced in [AIP-55](https://github.com/aptos-foundation/AIPs/pull/263)). Generating an account defaults to Legacy Ed25519 authentication with the option to use the Single Sender unified authentication. | ||
@@ -138,57 +126,90 @@ --- | ||
### Submit transaction | ||
### Submit transaction ([Tutorial](https://aptos.dev/en/build/sdks/ts-sdk/building-transactions)) | ||
--- | ||
#### Single Signer transaction | ||
Using transaction submission api | ||
```ts | ||
const alice: Account = Account.generate(); | ||
const bobAddress = "0xb0b"; | ||
// build transaction | ||
const transaction = await aptos.transaction.build.simple({ | ||
sender: alice.accountAddress, | ||
data: { | ||
function: "0x1::coin::transfer", | ||
typeArguments: ["0x1::aptos_coin::AptosCoin"], | ||
functionArguments: [bobAddress, 100], | ||
}, | ||
}); | ||
// using sign and submit separately | ||
const senderAuthenticator = aptos.transaction.sign({ signer: alice, transaction }); | ||
const committedTransaction = await aptos.transaction.submit.simple({ transaction, senderAuthenticator }); | ||
// using signAndSubmit combined | ||
const committedTransaction = await aptos.signAndSubmitTransaction({ signer: alice, transaction }); | ||
/** | ||
* This example shows how to use the Aptos SDK to send a transaction. | ||
* Don't forget to install @aptos-labs/ts-sdk before running this example! | ||
*/ | ||
import { | ||
Account, | ||
Aptos, | ||
AptosConfig, | ||
Network, | ||
} from "@aptos-labs/ts-sdk"; | ||
async function example() { | ||
console.log("This example will create two accounts (Alice and Bob) and send a transaction transfering APT to Bob's account."); | ||
// 0. Setup the client and test accounts | ||
const config = new AptosConfig({ network: Network.TESTNET }); | ||
const aptos = new Aptos(config); | ||
let alice = Account.generate(); | ||
let bob = Account.generate(); | ||
console.log("=== Addresses ===\n"); | ||
console.log(`Alice's address is: ${alice.accountAddress}`); | ||
console.log(`Bob's address is: ${bob.accountAddress}`); | ||
console.log("\n=== Funding accounts ===\n"); | ||
await aptos.fundAccount({ | ||
accountAddress: alice.accountAddress, | ||
amount: 100_000_000, | ||
}); | ||
await aptos.fundAccount({ | ||
accountAddress: bob.accountAddress, | ||
amount: 100, | ||
}); | ||
console.log("Funded Alice and Bob's accounts!") | ||
// 1. Build | ||
console.log("\n=== 1. Building the transaction ===\n"); | ||
const transaction = await aptos.transaction.build.simple({ | ||
sender: alice.accountAddress, | ||
data: { | ||
// All transactions on Aptos are implemented via smart contracts. | ||
function: "0x1::aptos_account::transfer", | ||
functionArguments: [bob.accountAddress, 100], | ||
}, | ||
}); | ||
console.log("Built the transaction!") | ||
// 2. Simulate (Optional) | ||
console.log("\n === 2. Simulating Response (Optional) === \n") | ||
const [userTransactionResponse] = await aptos.transaction.simulate.simple({ | ||
signerPublicKey: alice.publicKey, | ||
transaction, | ||
}); | ||
console.log(userTransactionResponse) | ||
// 3. Sign | ||
console.log("\n=== 3. Signing transaction ===\n"); | ||
const senderAuthenticator = aptos.transaction.sign({ | ||
signer: alice, | ||
transaction, | ||
}); | ||
console.log("Signed the transaction!") | ||
// 4. Submit | ||
console.log("\n=== 4. Submitting transaction ===\n"); | ||
const submittedTransaction = await aptos.transaction.submit.simple({ | ||
transaction, | ||
senderAuthenticator, | ||
}); | ||
console.log(`Submitted transaction hash: ${submittedTransaction.hash}`); | ||
// 5. Wait for results | ||
console.log("\n=== 5. Waiting for result of transaction ===\n"); | ||
const executedTransaction = await aptos.waitForTransaction({ transactionHash: submittedTransaction.hash }); | ||
console.log(executedTransaction) | ||
}; | ||
example(); | ||
``` | ||
Using built in `transferCoinTransaction` | ||
```ts | ||
const alice: Account = Account.generate(); | ||
const bobAddress = "0xb0b"; | ||
// build transaction | ||
const transaction = await aptos.transferCoinTransaction({ | ||
sender: alice, | ||
recipient: bobAddress, | ||
amount: 100, | ||
}); | ||
const pendingTransaction = await aptos.signAndSubmitTransaction({ signer: alice, transaction }); | ||
``` | ||
### Testing | ||
To run the SDK tests, simply run from the root of this repository: | ||
> Note: for a better experience, make sure there is no aptos local node process up and running (can check if there is a process running on port 8080). | ||
```bash | ||
pnpm i | ||
pnpm test | ||
``` | ||
## Troubleshooting | ||
@@ -202,3 +223,3 @@ | ||
It could be your `tsconfig.json` is not incompatible, make sure your `moduleResolution` is set to `node` instead of `bundler`. | ||
It could be your `tsconfig.json` is not using `node`. Make sure your `moduleResolution` in the `tsconfig.json` is set to `node` instead of `bundler`. | ||
@@ -208,4 +229,4 @@ ## Contributing | ||
If you found a bug or would like to request a feature, please file an [issue](https://github.com/aptos-labs/aptos-ts-sdk/issues/new/choose). | ||
If, based on the discussion on an issue you would like to offer a code change, please make a [pull request](https://github.com/aptos-labs/aptos-ts-sdk/CONTRIBUTING.md). | ||
If neither of these describes what you would like to contribute, checkout out the [contributing guide](https://github.com/aptos-labs/aptos-ts-sdk/CONTRIBUTING.md). | ||
If, based on the discussion on an issue you would like to offer a code change, please make a [pull request](https://github.com/aptos-labs/aptos-ts-sdk/pulls). | ||
If neither of these describes what you would like to contribute, checkout out the [contributing guide](https://github.com/aptos-labs/aptos-ts-sdk/blob/main/CONTRIBUTING.md). | ||
@@ -212,0 +233,0 @@ [npm-image-version]: https://img.shields.io/npm/v/%40aptos-labs%2Fts-sdk.svg |
@@ -6,5 +6,12 @@ // Copyright © Aptos Foundation | ||
import { Ed25519PrivateKey, EphemeralPublicKey, EphemeralSignature, PrivateKey } from "../core/crypto"; | ||
import { | ||
bytesToBigIntLE, | ||
padAndPackBytesWithLen, | ||
poseidonHash, | ||
Ed25519PrivateKey, | ||
EphemeralPublicKey, | ||
EphemeralSignature, | ||
PrivateKey, | ||
} from "../core/crypto"; | ||
import { Hex } from "../core/hex"; | ||
import { bytesToBigIntLE, padAndPackBytesWithLen, poseidonHash } from "../core/crypto/poseidon"; | ||
import { EphemeralPublicKeyVariant, HexInput } from "../types"; | ||
@@ -11,0 +18,0 @@ import { Deserializer, Serializable, Serializer } from "../bcs"; |
@@ -6,2 +6,4 @@ export * from "./Ed25519Account"; | ||
export * from "./KeylessAccount"; | ||
export * from "./AbstractKeylessAccount"; | ||
export * from "./FederatedKeylessAccount"; | ||
export * from "./MultiKeyAccount"; |
@@ -5,23 +5,9 @@ // Copyright © Aptos Foundation | ||
import { JwtPayload, jwtDecode } from "jwt-decode"; | ||
import EventEmitter from "eventemitter3"; | ||
import { EphemeralCertificateVariant, HexInput, SigningScheme } from "../types"; | ||
import { HexInput } from "../types"; | ||
import { AccountAddress } from "../core/accountAddress"; | ||
import { | ||
AnyPublicKey, | ||
AnySignature, | ||
KeylessPublicKey, | ||
KeylessSignature, | ||
EphemeralCertificate, | ||
ZeroKnowledgeSig, | ||
ZkProof, | ||
} from "../core/crypto"; | ||
import { ZeroKnowledgeSig } from "../core/crypto"; | ||
import { Account } from "./Account"; | ||
import { EphemeralKeyPair } from "./EphemeralKeyPair"; | ||
import { Hex } from "../core/hex"; | ||
import { AccountAuthenticatorSingleKey } from "../transactions/authenticator/account"; | ||
import { Deserializer, Serializable, Serializer } from "../bcs"; | ||
import { deriveTransactionType, generateSigningMessage } from "../transactions/transactionBuilder/signingMessage"; | ||
import { AnyRawTransaction, AnyRawTransactionInstance } from "../transactions/types"; | ||
import { base64UrlDecode } from "../utils/helpers"; | ||
import { Deserializer, Serializer } from "../bcs"; | ||
import { AbstractKeylessAccount, ProofFetchCallback } from "./AbstractKeylessAccount"; | ||
@@ -33,3 +19,3 @@ /** | ||
* | ||
* Use KeylessAccount.fromJWTAndProof to instantiate a KeylessAccount with a JWT, proof and EphemeralKeyPair. | ||
* Use KeylessAccount.create to instantiate a KeylessAccount with a JWT, proof and EphemeralKeyPair. | ||
* | ||
@@ -39,67 +25,3 @@ * When the proof expires or the JWT becomes invalid, the KeylessAccount must be instantiated again with a new JWT, | ||
*/ | ||
export class KeylessAccount extends Serializable implements Account { | ||
static readonly PEPPER_LENGTH: number = 31; | ||
/** | ||
* The KeylessPublicKey associated with the account | ||
*/ | ||
readonly publicKey: KeylessPublicKey; | ||
/** | ||
* The EphemeralKeyPair used to generate sign. | ||
*/ | ||
readonly ephemeralKeyPair: EphemeralKeyPair; | ||
/** | ||
* The claim on the JWT to identify a user. This is typically 'sub' or 'email'. | ||
*/ | ||
readonly uidKey: string; | ||
/** | ||
* The value of the uidKey claim on the JWT. This intended to be a stable user identifier. | ||
*/ | ||
readonly uidVal: string; | ||
/** | ||
* The value of the 'aud' claim on the JWT, also known as client ID. This is the identifier for the dApp's | ||
* OIDC registration with the identity provider. | ||
*/ | ||
readonly aud: string; | ||
/** | ||
* A value contains 31 bytes of entropy that preserves privacy of the account. Typically fetched from a pepper provider. | ||
*/ | ||
readonly pepper: Uint8Array; | ||
/** | ||
* Account address associated with the account | ||
*/ | ||
readonly accountAddress: AccountAddress; | ||
/** | ||
* The zero knowledge signature (if ready) which contains the proof used to validate the EphemeralKeyPair. | ||
*/ | ||
proof: ZeroKnowledgeSig | undefined; | ||
/** | ||
* The proof of the EphemeralKeyPair or a promise that provides the proof. This is used to allow for awaiting on | ||
* fetching the proof. | ||
*/ | ||
readonly proofOrPromise: ZeroKnowledgeSig | Promise<ZeroKnowledgeSig>; | ||
/** | ||
* Signing scheme used to sign transactions | ||
*/ | ||
readonly signingScheme: SigningScheme; | ||
/** | ||
* The JWT token used to derive the account | ||
*/ | ||
readonly jwt: string; | ||
/** | ||
* An event emitter used to assist in handling asycronous proof fetching. | ||
*/ | ||
private readonly emitter: EventEmitter<ProofFetchEvents>; | ||
export class KeylessAccount extends AbstractKeylessAccount { | ||
// Use the static constructor 'create' instead. | ||
@@ -118,50 +40,5 @@ private constructor(args: { | ||
}) { | ||
super(); | ||
const { address, ephemeralKeyPair, uidKey, uidVal, aud, pepper, proof, proofFetchCallback, jwt } = args; | ||
this.ephemeralKeyPair = ephemeralKeyPair; | ||
this.publicKey = KeylessPublicKey.create(args); | ||
this.accountAddress = address ? AccountAddress.from(address) : this.publicKey.authKey().derivedAddress(); | ||
this.uidKey = uidKey; | ||
this.uidVal = uidVal; | ||
this.aud = aud; | ||
this.jwt = jwt; | ||
this.emitter = new EventEmitter<ProofFetchEvents>(); | ||
this.proofOrPromise = proof; | ||
if (proof instanceof ZeroKnowledgeSig) { | ||
this.proof = proof; | ||
} else { | ||
if (proofFetchCallback === undefined) { | ||
throw new Error("Must provide callback for async proof fetch"); | ||
} | ||
this.emitter.on("proofFetchFinish", async (status) => { | ||
await proofFetchCallback(status); | ||
this.emitter.removeAllListeners(); | ||
}); | ||
this.init(proof); | ||
} | ||
this.signingScheme = SigningScheme.SingleKey; | ||
const pepperBytes = Hex.fromHexInput(pepper).toUint8Array(); | ||
if (pepperBytes.length !== KeylessAccount.PEPPER_LENGTH) { | ||
throw new Error(`Pepper length in bytes should be ${KeylessAccount.PEPPER_LENGTH}`); | ||
} | ||
this.pepper = pepperBytes; | ||
super(args); | ||
} | ||
/** | ||
* This initializes the asyncronous proof fetch | ||
* @return | ||
*/ | ||
async init(promise: Promise<ZeroKnowledgeSig>) { | ||
try { | ||
this.proof = await promise; | ||
this.emitter.emit("proofFetchFinish", { status: "Success" }); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
this.emitter.emit("proofFetchFinish", { status: "Failed", error: error.toString() }); | ||
} else { | ||
this.emitter.emit("proofFetchFinish", { status: "Failed", error: "Unknown" }); | ||
} | ||
} | ||
} | ||
serialize(serializer: Serializer): void { | ||
@@ -173,3 +50,3 @@ serializer.serializeStr(this.jwt); | ||
if (this.proof === undefined) { | ||
throw new Error("Connot serialize - proof undefined"); | ||
throw new Error("Cannot serialize - proof undefined"); | ||
} | ||
@@ -194,106 +71,2 @@ this.proof.serialize(serializer); | ||
/** | ||
* Checks if the proof is expired. If so the account must be rederived with a new EphemeralKeyPair | ||
* and JWT token. | ||
* @return boolean | ||
*/ | ||
isExpired(): boolean { | ||
return this.ephemeralKeyPair.isExpired(); | ||
} | ||
/** | ||
* Sign a message using Keyless. | ||
* @param message the message to sign, as binary input | ||
* @return the AccountAuthenticator containing the signature, together with the account's public key | ||
*/ | ||
signWithAuthenticator(message: HexInput): AccountAuthenticatorSingleKey { | ||
const signature = new AnySignature(this.sign(message)); | ||
const publicKey = new AnyPublicKey(this.publicKey); | ||
return new AccountAuthenticatorSingleKey(publicKey, signature); | ||
} | ||
/** | ||
* Sign a transaction using Keyless. | ||
* @param transaction the raw transaction | ||
* @return the AccountAuthenticator containing the signature of the transaction, together with the account's public key | ||
*/ | ||
signTransactionWithAuthenticator(transaction: AnyRawTransaction): AccountAuthenticatorSingleKey { | ||
const signature = new AnySignature(this.signTransaction(transaction)); | ||
const publicKey = new AnyPublicKey(this.publicKey); | ||
return new AccountAuthenticatorSingleKey(publicKey, signature); | ||
} | ||
/** | ||
* Waits for asyncronous proof fetching to finish. | ||
* @return | ||
*/ | ||
async waitForProofFetch() { | ||
if (this.proofOrPromise instanceof Promise) { | ||
await this.proofOrPromise; | ||
} | ||
} | ||
/** | ||
* Sign the given message using Keyless. | ||
* @param message in HexInput format | ||
* @returns Signature | ||
*/ | ||
sign(data: HexInput): KeylessSignature { | ||
const { expiryDateSecs } = this.ephemeralKeyPair; | ||
if (this.isExpired()) { | ||
throw new Error("EphemeralKeyPair is expired"); | ||
} | ||
if (this.proof === undefined) { | ||
throw new Error("Proof not defined"); | ||
} | ||
const ephemeralPublicKey = this.ephemeralKeyPair.getPublicKey(); | ||
const ephemeralSignature = this.ephemeralKeyPair.sign(data); | ||
return new KeylessSignature({ | ||
jwtHeader: base64UrlDecode(this.jwt.split(".")[0]), | ||
ephemeralCertificate: new EphemeralCertificate(this.proof, EphemeralCertificateVariant.ZkProof), | ||
expiryDateSecs, | ||
ephemeralPublicKey, | ||
ephemeralSignature, | ||
}); | ||
} | ||
/** | ||
* Sign the given transaction with Keyless. | ||
* Signs the transaction and proof to guard against proof malleability. | ||
* @param transaction the transaction to be signed | ||
* @returns KeylessSignature | ||
*/ | ||
signTransaction(transaction: AnyRawTransaction): KeylessSignature { | ||
if (this.proof === undefined) { | ||
throw new Error("Proof not found"); | ||
} | ||
const raw = deriveTransactionType(transaction); | ||
const txnAndProof = new TransactionAndProof(raw, this.proof.proof); | ||
const signMess = txnAndProof.hash(); | ||
return this.sign(signMess); | ||
} | ||
/** | ||
* Note - This function is currently incomplete and should only be used to verify ownership of the KeylessAccount | ||
* | ||
* Verifies a signature given the message. | ||
* | ||
* TODO: Groth16 proof verification | ||
* | ||
* @param args.message the message that was signed. | ||
* @param args.signature the KeylessSignature to verify | ||
* @returns boolean | ||
*/ | ||
verifySignature(args: { message: HexInput; signature: KeylessSignature }): boolean { | ||
const { message, signature } = args; | ||
if (this.isExpired()) { | ||
return false; | ||
} | ||
if (!this.ephemeralKeyPair.getPublicKey().verifySignature({ message, signature: signature.ephemeralSignature })) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
static fromBytes(bytes: Uint8Array): KeylessAccount { | ||
@@ -315,7 +88,8 @@ return KeylessAccount.deserialize(new Deserializer(bytes)); | ||
const jwtPayload = jwtDecode<JwtPayload & { [key: string]: string }>(jwt); | ||
const iss = jwtPayload.iss!; | ||
if (typeof jwtPayload.iss !== "string") { | ||
throw new Error("iss was not found"); | ||
} | ||
if (typeof jwtPayload.aud !== "string") { | ||
throw new Error("aud was not found or an array of values"); | ||
} | ||
const aud = jwtPayload.aud!; | ||
const uidVal = jwtPayload[uidKey]; | ||
@@ -326,6 +100,6 @@ return new KeylessAccount({ | ||
ephemeralKeyPair, | ||
iss, | ||
iss: jwtPayload.iss, | ||
uidKey, | ||
uidVal, | ||
aud, | ||
aud: jwtPayload.aud, | ||
pepper, | ||
@@ -337,59 +111,1 @@ jwt, | ||
} | ||
/** | ||
* A container class to hold a transaction and a proof. It implements CryptoHashable which is used to create | ||
* the signing message for Keyless transactions. We sign over the proof to ensure non-malleability. | ||
*/ | ||
class TransactionAndProof extends Serializable { | ||
/** | ||
* The transaction to sign. | ||
*/ | ||
transaction: AnyRawTransactionInstance; | ||
/** | ||
* The zero knowledge proof used in signing the transaction. | ||
*/ | ||
proof?: ZkProof; | ||
/** | ||
* The domain separator prefix used when hashing. | ||
*/ | ||
readonly domainSeparator = "APTOS::TransactionAndProof"; | ||
constructor(transaction: AnyRawTransactionInstance, proof?: ZkProof) { | ||
super(); | ||
this.transaction = transaction; | ||
this.proof = proof; | ||
} | ||
serialize(serializer: Serializer): void { | ||
serializer.serializeFixedBytes(this.transaction.bcsToBytes()); | ||
serializer.serializeOption(this.proof); | ||
} | ||
/** | ||
* Hashes the bcs serialized from of the class. This is the typescript corollary to the BCSCryptoHash macro in aptos-core. | ||
* | ||
* @returns Uint8Array | ||
*/ | ||
hash(): Uint8Array { | ||
return generateSigningMessage(this.bcsToBytes(), this.domainSeparator); | ||
} | ||
} | ||
export type ProofFetchSuccess = { | ||
status: "Success"; | ||
}; | ||
export type ProofFetchFailure = { | ||
status: "Failed"; | ||
error: string; | ||
}; | ||
export type ProofFetchStatus = ProofFetchSuccess | ProofFetchFailure; | ||
export type ProofFetchCallback = (status: ProofFetchStatus) => Promise<void>; | ||
export interface ProofFetchEvents { | ||
proofFetchFinish: (status: ProofFetchStatus) => void; | ||
} |
@@ -10,3 +10,3 @@ // Copyright © Aptos Foundation | ||
import { AnyRawTransaction } from "../transactions/types"; | ||
import { KeylessAccount } from "./KeylessAccount"; | ||
import { AbstractKeylessAccount } from "./AbstractKeylessAccount"; | ||
@@ -134,3 +134,5 @@ export interface VerifyMultiKeySignatureArgs { | ||
async waitForProofFetch() { | ||
const keylessSigners = this.signers.filter((signer) => signer instanceof KeylessAccount) as KeylessAccount[]; | ||
const keylessSigners = this.signers.filter( | ||
(signer) => signer instanceof AbstractKeylessAccount, | ||
) as AbstractKeylessAccount[]; | ||
const promises = keylessSigners.map(async (signer) => signer.waitForProofFetch()); | ||
@@ -137,0 +139,0 @@ await Promise.all(promises); |
@@ -5,3 +5,3 @@ // Copyright © Aptos Foundation | ||
import { Account as AccountModule } from "../account"; | ||
import { AccountAddress, PrivateKey, AccountAddressInput } from "../core"; | ||
import { AccountAddress, PrivateKey, AccountAddressInput, createObjectAddress } from "../core"; | ||
import { | ||
@@ -28,3 +28,2 @@ AccountData, | ||
deriveAccountFromPrivateKey, | ||
getAccountCoinAmount, | ||
getAccountCoinsCount, | ||
@@ -52,2 +51,3 @@ getAccountCoinsData, | ||
import { isEncodedStruct, parseEncodedStruct } from "../utils"; | ||
import { memoizeAsync } from "../utils/memoize"; | ||
@@ -460,8 +460,3 @@ /** | ||
}): Promise<number> { | ||
const { coinType, faMetadataAddress } = args; | ||
await waitForIndexerOnVersion({ | ||
config: this.config, | ||
minimumLedgerVersion: args.minimumLedgerVersion, | ||
processorType: ProcessorType.FUNGIBLE_ASSET_PROCESSOR, | ||
}); | ||
const { accountAddress, coinType, faMetadataAddress } = args; | ||
@@ -472,20 +467,71 @@ // Attempt to populate the CoinType field if the FA address is provided. | ||
if (coinType === undefined && faMetadataAddress !== undefined) { | ||
try { | ||
const pairedCoinTypeStruct = ( | ||
await view({ | ||
aptosConfig: this.config, | ||
payload: { function: "0x1::coin::paired_coin", functionArguments: [faMetadataAddress] }, | ||
}) | ||
).at(0) as { vec: MoveValue[] }; | ||
coinAssetType = await memoizeAsync( | ||
async () => { | ||
try { | ||
const pairedCoinTypeStruct = ( | ||
await view({ | ||
aptosConfig: this.config, | ||
payload: { function: "0x1::coin::paired_coin", functionArguments: [faMetadataAddress] }, | ||
}) | ||
).at(0) as { vec: MoveValue[] }; | ||
// Check if the Option has a value, and if so, parse the struct | ||
if (pairedCoinTypeStruct.vec.length > 0 && isEncodedStruct(pairedCoinTypeStruct.vec[0])) { | ||
coinAssetType = parseEncodedStruct(pairedCoinTypeStruct.vec[0]); | ||
} | ||
} catch (error) { | ||
/* No paired coin type found */ | ||
// Check if the Option has a value, and if so, parse the struct | ||
if (pairedCoinTypeStruct.vec.length > 0 && isEncodedStruct(pairedCoinTypeStruct.vec[0])) { | ||
return parseEncodedStruct(pairedCoinTypeStruct.vec[0]) as MoveStructId; | ||
} | ||
} catch (error) { | ||
/* No paired coin type found */ | ||
} | ||
return undefined; | ||
}, | ||
`coin-mapping-${faMetadataAddress.toString()}`, | ||
1000 * 60 * 5, // 5 minutes | ||
)(); | ||
} | ||
let faAddress: string; | ||
if (coinType !== undefined && faMetadataAddress !== undefined) { | ||
faAddress = AccountAddress.from(faMetadataAddress).toStringLong(); | ||
} else if (coinType !== undefined && faMetadataAddress === undefined) { | ||
// TODO Move to a separate function as defined in the AIP for coin migration | ||
if (coinType === APTOS_COIN) { | ||
faAddress = AccountAddress.A.toStringLong(); | ||
} else { | ||
faAddress = createObjectAddress(AccountAddress.A, coinType).toStringLong(); | ||
} | ||
} else if (coinType === undefined && faMetadataAddress !== undefined) { | ||
const addr = AccountAddress.from(faMetadataAddress); | ||
faAddress = addr.toStringLong(); | ||
if (addr === AccountAddress.A) { | ||
coinAssetType = APTOS_COIN; | ||
} | ||
// The paired CoinType should be populated outside of this function in another | ||
// async call. We cannot do this internally due to dependency cycles issue. | ||
} else { | ||
throw new Error("Either coinType, faMetadataAddress, or both must be provided"); | ||
} | ||
return getAccountCoinAmount({ aptosConfig: this.config, ...args, coinType: coinAssetType }); | ||
// When there is a coin mapping, use that first, otherwise use the fungible asset address | ||
// TODO: This function's signature at the top, returns number, but it could be greater than can be represented | ||
if (coinAssetType !== undefined) { | ||
const [balanceStr] = await view<[string]>({ | ||
aptosConfig: this.config, | ||
payload: { | ||
function: "0x1::coin::balance", | ||
typeArguments: [coinAssetType], | ||
functionArguments: [accountAddress], | ||
}, | ||
}); | ||
return parseInt(balanceStr, 10); | ||
} | ||
const [balanceStr] = await view<[string]>({ | ||
aptosConfig: this.config, | ||
payload: { | ||
function: "0x1::primary_fungible_store::balance", | ||
typeArguments: ["0x1::object::ObjectCore"], | ||
functionArguments: [accountAddress, faAddress], | ||
}, | ||
}); | ||
return parseInt(balanceStr, 10); | ||
} | ||
@@ -492,0 +538,0 @@ |
// Copyright © Aptos Foundation | ||
// SPDX-License-Identifier: Apache-2.0 | ||
import { EphemeralKeyPair, KeylessAccount, ProofFetchCallback } from "../account"; | ||
import { ZeroKnowledgeSig } from "../core"; | ||
import { deriveKeylessAccount, getPepper, getProof } from "../internal/keyless"; | ||
import { Account, EphemeralKeyPair, KeylessAccount, ProofFetchCallback } from "../account"; | ||
import { FederatedKeylessAccount } from "../account/FederatedKeylessAccount"; | ||
import { AccountAddressInput, ZeroKnowledgeSig } from "../core"; | ||
import { | ||
deriveKeylessAccount, | ||
getPepper, | ||
getProof, | ||
updateFederatedKeylessJwkSetTransaction, | ||
} from "../internal/keyless"; | ||
import { SimpleTransaction } from "../transactions"; | ||
import { HexInput } from "../types"; | ||
@@ -55,2 +62,19 @@ import { AptosConfig } from "./aptosConfig"; | ||
async deriveKeylessAccount(args: { | ||
jwt: string; | ||
ephemeralKeyPair: EphemeralKeyPair; | ||
uidKey?: string; | ||
pepper?: HexInput; | ||
proofFetchCallback?: ProofFetchCallback; | ||
}): Promise<KeylessAccount>; | ||
async deriveKeylessAccount(args: { | ||
jwt: string; | ||
ephemeralKeyPair: EphemeralKeyPair; | ||
jwkAddress: AccountAddressInput; | ||
uidKey?: string; | ||
pepper?: HexInput; | ||
proofFetchCallback?: ProofFetchCallback; | ||
}): Promise<FederatedKeylessAccount>; | ||
/** | ||
@@ -62,2 +86,3 @@ * Derives the Keyless Account from the JWT token and corresponding EphemeralKeyPair. It will lookup the pepper from | ||
* @param args.ephemeralKeyPair the EphemeralKeyPair used to generate the nonce in the JWT token | ||
* @param args.jwkAddress the where the JWKs used to verify signatures are found. Setting the value derives a FederatedKeylessAccount | ||
* @param args.uidKey a key in the JWT token to use to set the uidVal in the IdCommitment | ||
@@ -74,8 +99,29 @@ * @param args.pepper the pepper | ||
ephemeralKeyPair: EphemeralKeyPair; | ||
jwkAddress?: AccountAddressInput; | ||
uidKey?: string; | ||
pepper?: HexInput; | ||
proofFetchCallback?: ProofFetchCallback; | ||
}): Promise<KeylessAccount> { | ||
}): Promise<KeylessAccount | FederatedKeylessAccount> { | ||
return deriveKeylessAccount({ aptosConfig: this.config, ...args }); | ||
} | ||
/** | ||
* This installs a set of FederatedJWKs at an address for a given iss. | ||
* | ||
* It will fetch the JWK set from the well-known endpoint and update the FederatedJWKs at the sender's address | ||
* to reflect it. | ||
* | ||
* @param args.sender The account that will install the JWKs | ||
* @param args.iss the iss claim of the federated OIDC provider. | ||
* @param args.jwksUrl the URL to find the corresponding JWKs. For supported IDP providers this parameter in not necessary. | ||
* | ||
* @returns The pending transaction that results from submission. | ||
*/ | ||
async updateFederatedKeylessJwkSetTransaction(args: { | ||
sender: Account; | ||
iss: string; | ||
jwksUrl?: string; | ||
}): Promise<SimpleTransaction> { | ||
return updateFederatedKeylessJwkSetTransaction({ aptosConfig: this.config, ...args }); | ||
} | ||
} |
@@ -24,6 +24,9 @@ // Copyright © Aptos Foundation | ||
import { | ||
FeePayerOrFeePayerAuthenticatorOrNeither, | ||
getSigningMessage, | ||
publicPackageTransaction, | ||
rotateAuthKey, | ||
signAndSubmitAsFeePayer, | ||
signAndSubmitTransaction, | ||
signAsFeePayer, | ||
signTransaction, | ||
@@ -294,16 +297,4 @@ } from "../internal/transactionSubmission"; | ||
signAsFeePayer(args: { signer: Account; transaction: AnyRawTransaction }): AccountAuthenticator { | ||
const { signer, transaction } = args; | ||
// if transaction doesnt hold a "feePayerAddress" prop it means | ||
// this is not a fee payer transaction | ||
if (!transaction.feePayerAddress) { | ||
throw new Error(`Transaction ${transaction} is not a Fee Payer transaction`); | ||
} | ||
// Set the feePayerAddress to the signer account address | ||
transaction.feePayerAddress = signer.accountAddress; | ||
return signTransaction({ | ||
signer, | ||
transaction, | ||
return signAsFeePayer({ | ||
...args, | ||
}); | ||
@@ -360,13 +351,42 @@ } | ||
*/ | ||
async signAndSubmitTransaction(args: { | ||
signer: Account; | ||
async signAndSubmitTransaction( | ||
args: FeePayerOrFeePayerAuthenticatorOrNeither & { | ||
signer: Account; | ||
transaction: AnyRawTransaction; | ||
}, | ||
): Promise<PendingTransactionResponse> { | ||
return signAndSubmitTransaction({ | ||
aptosConfig: this.config, | ||
...args, | ||
}); | ||
} | ||
/** | ||
* Sign and submit a single signer transaction as the fee payer to chain given an authenticator by the sender of the transaction. | ||
* | ||
* @param args.feePayer The fee payer account to sign the transaction | ||
* @param args.senderAuthenticator The AccountAuthenticator signed by the sender of the transaction | ||
* @param args.transaction An instance of a RawTransaction, plus optional secondary/fee payer addresses | ||
* | ||
* @example | ||
* const transaction = await aptos.transaction.build.simple({sender: alice.accountAddress, feePayer: true ...}) | ||
* const senderAuthenticator = alice.signTransactionWithAuthenticator(transaction) | ||
* const pendingTransaction = await aptos.signAndSubmitAsFeePayer({ | ||
* senderAuthenticator, | ||
* feePayer: bob, | ||
* transaction, | ||
* }) | ||
* | ||
* @return PendingTransactionResponse | ||
*/ | ||
async signAndSubmitAsFeePayer(args: { | ||
feePayer: Account; | ||
senderAuthenticator: AccountAuthenticator; | ||
transaction: AnyRawTransaction; | ||
}): Promise<PendingTransactionResponse> { | ||
const { signer, transaction } = args; | ||
return signAndSubmitTransaction({ | ||
return signAndSubmitAsFeePayer({ | ||
aptosConfig: this.config, | ||
signer, | ||
transaction, | ||
...args, | ||
}); | ||
} | ||
} |
@@ -9,3 +9,5 @@ // Copyright © Aptos Foundation | ||
export * from "./ephemeral"; | ||
export * from "./federatedKeyless"; | ||
export * from "./keyless"; | ||
export * from "./poseidon"; | ||
export * from "./privateKey"; | ||
@@ -12,0 +14,0 @@ export * from "./publicKey"; |
@@ -161,9 +161,10 @@ // Copyright © Aptos Foundation | ||
const jwtPayload = jwtDecode<JwtPayload & { [key: string]: string }>(jwt); | ||
const iss = jwtPayload.iss!; | ||
if (typeof jwtPayload.iss !== "string") { | ||
throw new Error("iss was not found"); | ||
} | ||
if (typeof jwtPayload.aud !== "string") { | ||
throw new Error("aud was not found or an array of values"); | ||
} | ||
const aud = jwtPayload.aud!; | ||
const uidVal = jwtPayload[uidKey]; | ||
return KeylessPublicKey.create({ iss, uidKey, uidVal, aud, pepper }); | ||
return KeylessPublicKey.create({ iss: jwtPayload.iss, uidKey, uidVal, aud: jwtPayload.aud, pepper }); | ||
} | ||
@@ -170,0 +171,0 @@ |
@@ -101,8 +101,8 @@ /* eslint-disable no-bitwise */ | ||
export function bigIntToBytesLE(value: bigint, length: number): Uint8Array { | ||
export function bigIntToBytesLE(value: bigint | number, length: number): Uint8Array { | ||
let val = BigInt(value); | ||
const bytes = new Uint8Array(length); | ||
for (let i = 0; i < length; i += 1) { | ||
bytes[i] = Number(value & BigInt(0xff)); | ||
// eslint-disable-next-line no-param-reassign | ||
value >>= BigInt(8); | ||
bytes[i] = Number(val & BigInt(0xff)); | ||
val >>= BigInt(8); | ||
} | ||
@@ -109,0 +109,0 @@ return bytes; |
@@ -9,2 +9,3 @@ import { Deserializer, Serializer } from "../../bcs"; | ||
import { Signature } from "./signature"; | ||
import { FederatedKeylessPublicKey } from "./federatedKeyless"; | ||
@@ -41,2 +42,4 @@ /** | ||
this.variant = AnyPublicKeyVariant.Keyless; | ||
} else if (publicKey instanceof FederatedKeylessPublicKey) { | ||
this.variant = AnyPublicKeyVariant.FederatedKeyless; | ||
} else { | ||
@@ -96,2 +99,5 @@ throw new Error("Unsupported public key type"); | ||
break; | ||
case AnyPublicKeyVariant.FederatedKeyless: | ||
publicKey = FederatedKeylessPublicKey.deserialize(deserializer); | ||
break; | ||
default: | ||
@@ -98,0 +104,0 @@ throw new Error(`Unknown variant index for AnyPublicKey: ${variantIndex}`); |
@@ -15,5 +15,5 @@ import { HexInput } from "../../types"; | ||
const isValid = Hex.isValid(message); | ||
// If message is not a valid Hex string, convert it into a Buffer | ||
// If message is not a valid Hex string, convert it | ||
if (!isValid.valid) { | ||
return Buffer.from(message, "utf8"); | ||
return new TextEncoder().encode(message); | ||
} | ||
@@ -20,0 +20,0 @@ // If message is a valid Hex string, return it |
@@ -55,6 +55,5 @@ // Copyright © Aptos Foundation | ||
import { memoizeAsync } from "../utils/memoize"; | ||
import { Secp256k1PrivateKey, AuthenticationKey, Ed25519PrivateKey, createObjectAddress } from "../core"; | ||
import { Secp256k1PrivateKey, AuthenticationKey, Ed25519PrivateKey } from "../core"; | ||
import { CurrentFungibleAssetBalancesBoolExp } from "../types/generated/types"; | ||
import { getTableItem } from "./table"; | ||
import { APTOS_COIN } from "../utils"; | ||
@@ -406,54 +405,2 @@ export async function getInfo(args: { | ||
export async function getAccountCoinAmount(args: { | ||
aptosConfig: AptosConfig; | ||
accountAddress: AccountAddressInput; | ||
coinType?: MoveStructId; | ||
faMetadataAddress?: AccountAddressInput; | ||
}): Promise<number> { | ||
const { aptosConfig, accountAddress, coinType, faMetadataAddress } = args; | ||
let coinAssetType: string | undefined = coinType; | ||
let faAddress: string; | ||
if (coinType !== undefined && faMetadataAddress !== undefined) { | ||
faAddress = AccountAddress.from(faMetadataAddress).toStringLong(); | ||
} else if (coinType !== undefined && faMetadataAddress === undefined) { | ||
// TODO Move to a separate function as defined in the AIP for coin migration | ||
if (coinType === APTOS_COIN) { | ||
faAddress = AccountAddress.A.toStringLong(); | ||
} else { | ||
faAddress = createObjectAddress(AccountAddress.A, coinType).toStringLong(); | ||
} | ||
} else if (coinType === undefined && faMetadataAddress !== undefined) { | ||
const addr = AccountAddress.from(faMetadataAddress); | ||
faAddress = addr.toStringLong(); | ||
if (addr === AccountAddress.A) { | ||
coinAssetType = APTOS_COIN; | ||
} | ||
// The paired CoinType should be populated outside of this function in another | ||
// async call. We cannot do this internally due to dependency cycles issue. | ||
} else { | ||
throw new Error("Either coinType, fungibleAssetAddress, or both must be provided"); | ||
} | ||
const address = AccountAddress.from(accountAddress).toStringLong(); | ||
// Search by fungible asset address, unless it has a coin it migrated from | ||
let where: any = { asset_type: { _eq: faAddress } }; | ||
if (coinAssetType !== undefined) { | ||
where = { asset_type: { _in: [coinAssetType, faAddress] } }; | ||
} | ||
const data = await getAccountCoinsData({ | ||
aptosConfig, | ||
accountAddress: address, | ||
options: { | ||
where, | ||
}, | ||
}); | ||
// commonjs (aka cjs) doesnt handle Nullish Coalescing for some reason | ||
// might be because of how ts infer the graphql generated scheme type | ||
return data[0] ? data[0].amount : 0; | ||
} | ||
export async function getAccountCoinsData(args: { | ||
@@ -460,0 +407,0 @@ aptosConfig: AptosConfig; |
@@ -10,5 +10,7 @@ // Copyright © Aptos Foundation | ||
*/ | ||
import { jwtDecode, JwtPayload } from "jwt-decode"; | ||
import { AptosConfig } from "../api/aptosConfig"; | ||
import { postAptosPepperService, postAptosProvingService } from "../client"; | ||
import { | ||
AccountAddressInput, | ||
EphemeralSignature, | ||
@@ -23,6 +25,10 @@ Groth16Zkp, | ||
import { HexInput, ZkpVariant } from "../types"; | ||
import { EphemeralKeyPair, KeylessAccount, ProofFetchCallback } from "../account"; | ||
import { Account, EphemeralKeyPair, KeylessAccount, ProofFetchCallback } from "../account"; | ||
import { PepperFetchRequest, PepperFetchResponse, ProverRequest, ProverResponse } from "../types/keyless"; | ||
import { nowInSeconds } from "../utils/helpers"; | ||
import { lookupOriginalAccountAddress } from "./account"; | ||
import { FederatedKeylessPublicKey } from "../core/crypto/federatedKeyless"; | ||
import { FederatedKeylessAccount } from "../account/FederatedKeylessAccount"; | ||
import { MoveVector } from "../bcs"; | ||
import { generateTransaction } from "./transactionSubmission"; | ||
import { SimpleTransaction } from "../transactions"; | ||
@@ -68,3 +74,7 @@ export async function getPepper(args: { | ||
const { maxExpHorizonSecs } = await getKeylessConfig({ aptosConfig }); | ||
if (maxExpHorizonSecs < ephemeralKeyPair.expiryDateSecs - nowInSeconds()) { | ||
const decodedJwt = jwtDecode<JwtPayload>(jwt); | ||
if (typeof decodedJwt.iat !== "number") { | ||
throw new Error("iat was not found"); | ||
} | ||
if (maxExpHorizonSecs < ephemeralKeyPair.expiryDateSecs - decodedJwt.iat) { | ||
throw Error(`The EphemeralKeyPair is too long lived. It's lifespan must be less than ${maxExpHorizonSecs}`); | ||
@@ -112,4 +122,24 @@ } | ||
proofFetchCallback?: ProofFetchCallback; | ||
}): Promise<KeylessAccount> { | ||
const { aptosConfig, jwt, uidKey, proofFetchCallback, pepper = await getPepper(args) } = args; | ||
}): Promise<KeylessAccount>; | ||
export async function deriveKeylessAccount(args: { | ||
aptosConfig: AptosConfig; | ||
jwt: string; | ||
ephemeralKeyPair: EphemeralKeyPair; | ||
jwkAddress: AccountAddressInput; | ||
uidKey?: string; | ||
pepper?: HexInput; | ||
proofFetchCallback?: ProofFetchCallback; | ||
}): Promise<FederatedKeylessAccount>; | ||
export async function deriveKeylessAccount(args: { | ||
aptosConfig: AptosConfig; | ||
jwt: string; | ||
ephemeralKeyPair: EphemeralKeyPair; | ||
jwkAddress?: AccountAddressInput; | ||
uidKey?: string; | ||
pepper?: HexInput; | ||
proofFetchCallback?: ProofFetchCallback; | ||
}): Promise<KeylessAccount | FederatedKeylessAccount> { | ||
const { aptosConfig, jwt, jwkAddress, uidKey, proofFetchCallback, pepper = await getPepper(args) } = args; | ||
const proofPromise = getProof({ ...args, pepper }); | ||
@@ -123,3 +153,13 @@ // If a callback is provided, pass in the proof as a promise to KeylessAccount.create. This will make the proof be fetched in the | ||
// Look up the original address to handle key rotations | ||
// Look up the original address to handle key rotations and then instantiate the account. | ||
if (jwkAddress !== undefined) { | ||
const publicKey = FederatedKeylessPublicKey.fromJwtAndPepper({ jwt, pepper, jwkAddress, uidKey }); | ||
const address = await lookupOriginalAccountAddress({ | ||
aptosConfig, | ||
authenticationKey: publicKey.authKey().derivedAddress(), | ||
}); | ||
return FederatedKeylessAccount.create({ ...args, address, proof, pepper, proofFetchCallback, jwkAddress }); | ||
} | ||
const publicKey = KeylessPublicKey.fromJwtAndPepper({ jwt, pepper, uidKey }); | ||
@@ -130,6 +170,44 @@ const address = await lookupOriginalAccountAddress({ | ||
}); | ||
return KeylessAccount.create({ ...args, address, proof, pepper, proofFetchCallback }); | ||
} | ||
const keylessAccount = KeylessAccount.create({ ...args, address, proof, pepper, proofFetchCallback }); | ||
interface JWK { | ||
kty: string; // Key type | ||
kid: string; // Key ID | ||
alg: string; // Algorithm used with the key | ||
n: string; // Modulus (for RSA keys) | ||
e: string; // Exponent (for RSA keys) | ||
} | ||
return keylessAccount; | ||
interface JWKS { | ||
keys: JWK[]; | ||
} | ||
export async function updateFederatedKeylessJwkSetTransaction(args: { | ||
aptosConfig: AptosConfig; | ||
sender: Account; | ||
iss: string; | ||
jwksUrl?: string; | ||
}): Promise<SimpleTransaction> { | ||
const { aptosConfig, sender, iss } = args; | ||
const jwksUrl = args.jwksUrl ?? (iss.endsWith("/") ? `${iss}.well-known/jwks.json` : `${iss}/.well-known/jwks.json`); | ||
const response = await fetch(jwksUrl); | ||
if (!response.ok) { | ||
throw new Error(`Failed to fetch JWKS: ${response.status} ${response.statusText}`); | ||
} | ||
const jwks: JWKS = await response.json(); | ||
return generateTransaction({ | ||
aptosConfig, | ||
sender: sender.accountAddress, | ||
data: { | ||
function: "0x1::jwks::update_federated_jwk_set", | ||
functionArguments: [ | ||
iss, | ||
MoveVector.MoveString(jwks.keys.map((key) => key.kid)), | ||
MoveVector.MoveString(jwks.keys.map((key) => key.alg)), | ||
MoveVector.MoveString(jwks.keys.map((key) => key.e)), | ||
MoveVector.MoveString(jwks.keys.map((key) => key.n)), | ||
], | ||
}, | ||
}); | ||
} |
@@ -11,3 +11,3 @@ /** | ||
import { postAptosFullNode } from "../client"; | ||
import { Account, KeylessAccount, MultiKeyAccount } from "../account"; | ||
import { Account, AbstractKeylessAccount, MultiKeyAccount } from "../account"; | ||
import { AccountAddress, AccountAddressInput } from "../core/accountAddress"; | ||
@@ -210,2 +210,20 @@ import { PrivateKey } from "../core/crypto"; | ||
export function signAsFeePayer(args: { signer: Account; transaction: AnyRawTransaction }): AccountAuthenticator { | ||
const { signer, transaction } = args; | ||
// if transaction doesnt hold a "feePayerAddress" prop it means | ||
// this is not a fee payer transaction | ||
if (!transaction.feePayerAddress) { | ||
throw new Error(`Transaction ${transaction} is not a Fee Payer transaction`); | ||
} | ||
// Set the feePayerAddress to the signer account address | ||
transaction.feePayerAddress = signer.accountAddress; | ||
return signTransaction({ | ||
signer, | ||
transaction, | ||
}); | ||
} | ||
/** | ||
@@ -273,22 +291,57 @@ * Simulates a transaction before singing it. | ||
} | ||
export type FeePayerOrFeePayerAuthenticatorOrNeither = | ||
| { feePayer: Account; feePayerAuthenticator?: never } | ||
| { feePayer?: never; feePayerAuthenticator: AccountAuthenticator } | ||
| { feePayer?: never; feePayerAuthenticator?: never }; | ||
export async function signAndSubmitTransaction(args: { | ||
aptosConfig: AptosConfig; | ||
signer: Account; | ||
transaction: AnyRawTransaction; | ||
}): Promise<PendingTransactionResponse> { | ||
const { aptosConfig, signer, transaction } = args; | ||
export async function signAndSubmitTransaction( | ||
args: FeePayerOrFeePayerAuthenticatorOrNeither & { | ||
aptosConfig: AptosConfig; | ||
signer: Account; | ||
transaction: AnyRawTransaction; | ||
}, | ||
): Promise<PendingTransactionResponse> { | ||
const { aptosConfig, signer, feePayer, transaction } = args; | ||
// If the signer contains a KeylessAccount, await proof fetching in case the proof | ||
// was fetched asyncronously. | ||
if (signer instanceof KeylessAccount || signer instanceof MultiKeyAccount) { | ||
if (signer instanceof AbstractKeylessAccount || signer instanceof MultiKeyAccount) { | ||
await signer.waitForProofFetch(); | ||
} | ||
const authenticator = signTransaction({ signer, transaction }); | ||
if (feePayer instanceof AbstractKeylessAccount || feePayer instanceof MultiKeyAccount) { | ||
await feePayer.waitForProofFetch(); | ||
} | ||
const feePayerAuthenticator = | ||
args.feePayerAuthenticator || (feePayer && signAsFeePayer({ signer: feePayer, transaction })); | ||
const senderAuthenticator = signTransaction({ signer, transaction }); | ||
return submitTransaction({ | ||
aptosConfig, | ||
transaction, | ||
senderAuthenticator: authenticator, | ||
senderAuthenticator, | ||
feePayerAuthenticator, | ||
}); | ||
} | ||
export async function signAndSubmitAsFeePayer(args: { | ||
aptosConfig: AptosConfig; | ||
feePayer: Account; | ||
senderAuthenticator: AccountAuthenticator; | ||
transaction: AnyRawTransaction; | ||
}): Promise<PendingTransactionResponse> { | ||
const { aptosConfig, senderAuthenticator, feePayer, transaction } = args; | ||
if (feePayer instanceof AbstractKeylessAccount || feePayer instanceof MultiKeyAccount) { | ||
await feePayer.waitForProofFetch(); | ||
} | ||
const feePayerAuthenticator = signAsFeePayer({ signer: feePayer, transaction }); | ||
return submitTransaction({ | ||
aptosConfig, | ||
transaction, | ||
senderAuthenticator, | ||
feePayerAuthenticator, | ||
}); | ||
} | ||
const packagePublishAbi: EntryFunctionABI = { | ||
@@ -295,0 +348,0 @@ typeParameters: [], |
@@ -12,3 +12,10 @@ // Copyright © Aptos Foundation | ||
import { AccountAddress, AccountAddressInput, Hex, PublicKey } from "../../core"; | ||
import { AnyPublicKey, AnySignature, KeylessPublicKey, KeylessSignature, Secp256k1PublicKey } from "../../core/crypto"; | ||
import { | ||
AnyPublicKey, | ||
AnySignature, | ||
KeylessPublicKey, | ||
KeylessSignature, | ||
Secp256k1PublicKey, | ||
FederatedKeylessPublicKey, | ||
} from "../../core/crypto"; | ||
import { Ed25519PublicKey, Ed25519Signature } from "../../core/crypto/ed25519"; | ||
@@ -449,23 +456,27 @@ import { getInfo } from "../../internal/account"; | ||
export function getAuthenticatorForSimulation(publicKey: PublicKey) { | ||
// Wrap the public key types below with AnyPublicKey as they are only support through single sender. | ||
// Learn more about AnyPublicKey here - https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-55.md | ||
const convertToAnyPublicKey = | ||
KeylessPublicKey.isInstance(publicKey) || | ||
FederatedKeylessPublicKey.isInstance(publicKey) || | ||
Secp256k1PublicKey.isInstance(publicKey); | ||
const accountPublicKey = convertToAnyPublicKey ? new AnyPublicKey(publicKey) : publicKey; | ||
// No need to for the signature to be matching in scheme. All that matters for simulations is that it's not valid | ||
const invalidSignature = new Ed25519Signature(new Uint8Array(64)); | ||
if (Ed25519PublicKey.isInstance(publicKey)) { | ||
return new AccountAuthenticatorEd25519(publicKey, invalidSignature); | ||
if (Ed25519PublicKey.isInstance(accountPublicKey)) { | ||
return new AccountAuthenticatorEd25519(accountPublicKey, invalidSignature); | ||
} | ||
if (AnyPublicKey.isInstance(publicKey)) { | ||
if (KeylessPublicKey.isInstance(publicKey.publicKey)) { | ||
return new AccountAuthenticatorSingleKey(publicKey, new AnySignature(KeylessSignature.getSimulationSignature())); | ||
if (AnyPublicKey.isInstance(accountPublicKey)) { | ||
if (KeylessPublicKey.isInstance(accountPublicKey.publicKey)) { | ||
return new AccountAuthenticatorSingleKey( | ||
accountPublicKey, | ||
new AnySignature(KeylessSignature.getSimulationSignature()), | ||
); | ||
} | ||
return new AccountAuthenticatorSingleKey(publicKey, new AnySignature(invalidSignature)); | ||
return new AccountAuthenticatorSingleKey(accountPublicKey, new AnySignature(invalidSignature)); | ||
} | ||
// TODO: remove this, non-account public keys should never make it here | ||
if (KeylessPublicKey.isInstance(publicKey) || Secp256k1PublicKey.isInstance(publicKey)) { | ||
// eslint-disable-next-line no-console | ||
console.warn("Expected AccountPublicKey, but got PublicKey. Please wrap your public key with AnyPublicKey."); | ||
return new AccountAuthenticatorSingleKey(new AnyPublicKey(publicKey), new AnySignature(invalidSignature)); | ||
} | ||
// TODO add support for AnyMultiKey | ||
@@ -472,0 +483,0 @@ throw new Error("Unsupported public key"); |
@@ -113,2 +113,3 @@ // Copyright © Aptos Foundation | ||
Keyless = 3, | ||
FederatedKeyless = 4, | ||
} | ||
@@ -1018,2 +1019,8 @@ | ||
/** | ||
* Whether the struct is a module event (aka v2 event). This will be false for v1 | ||
* events because the value is derived from the #[event] attribute on the struct in | ||
* the Move source code. This attribute is only relevant for v2 events. | ||
*/ | ||
is_event: boolean; | ||
/** | ||
* Abilities associated with the struct | ||
@@ -1020,0 +1027,0 @@ */ |
@@ -9,2 +9,2 @@ // Copyright © Aptos Foundation | ||
*/ | ||
export const VERSION = "1.28.0"; | ||
export const VERSION = "1.29.0"; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5751272
800
43296
233
7