Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add setup claim function for ts-tests #2

Merged
merged 35 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6c0056d
initial commit
sgc-code Jun 5, 2024
363fc03
refactor
Leonard-Pat Jun 5, 2024
e04942d
Merge branch 'develop' into refactor/ts-tests
Leonard-Pat Jun 5, 2024
d313bd9
refactor tests
Leonard-Pat Jun 5, 2024
60e09cc
fix tests
Leonard-Pat Jun 5, 2024
55d3cc9
random values
Leonard-Pat Jun 5, 2024
222139d
cleanup lib
Leonard-Pat Jun 6, 2024
126d51e
remove more uneeded code
Leonard-Pat Jun 6, 2024
633aa2c
split up setup and added caching
Leonard-Pat Jun 6, 2024
b9e1a0c
Merge branch 'develop' into refactor/ts-tests
Leonard-Pat Jun 6, 2024
f1bb825
remove before function
Leonard-Pat Jun 6, 2024
28ed163
remove more lib functions
Leonard-Pat Jun 6, 2024
ace7a25
move to lib
Leonard-Pat Jun 6, 2024
2a884cb
unused var
Leonard-Pat Jun 6, 2024
a1a149f
split validation tests
Leonard-Pat Jun 6, 2024
5ed73a6
fixed import
Leonard-Pat Jun 6, 2024
c031352
Merge branch 'develop' into refactor/ts-tests
Leonard-Pat Jun 6, 2024
f8b40cc
setup function
Leonard-Pat Jun 6, 2024
0990486
claimInternal var change
Leonard-Pat Jun 6, 2024
48c3b2b
reorder claim.ts
Leonard-Pat Jun 6, 2024
c23cf51
better naming
Leonard-Pat Jun 6, 2024
2e395c6
Merge branch 'develop' into refactor/ts-tests
Leonard-Pat Jun 6, 2024
0eb6f40
only pass in pub/priv key
Leonard-Pat Jun 6, 2024
1f518b6
claim external update
Leonard-Pat Jun 6, 2024
9f50df2
comments
Leonard-Pat Jun 7, 2024
8653eb2
major restructure
Leonard-Pat Jun 7, 2024
c5bda7e
pausable
Leonard-Pat Jun 7, 2024
4fc7d61
fix (nearly) all broken tests
Leonard-Pat Jun 7, 2024
4a8542e
fix gas l2 using v3
gaetbout Jun 7, 2024
770e286
Merge branch 'develop' into refactor/ts-tests
Leonard-Pat Jun 7, 2024
846913e
update to latest devnet
gaetbout Jun 7, 2024
8c228d1
Merge pull request #10 from argentlabs/latest-devnet
sgc-code Jun 7, 2024
30b1781
fixed tests
Leonard-Pat Jun 7, 2024
906ce54
remove only
Leonard-Pat Jun 7, 2024
73d96a1
remove export
Leonard-Pat Jun 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Use the base image
FROM shardlabs/starknet-devnet-rs:c4185522228f61ba04619151eb5706d4610fb00f
FROM shardlabs/starknet-devnet-rs:bab781a018318df51adb20fc60716c8429ee89b0

# Expose port 5050
EXPOSE 5050

# Set default command to run the container
CMD ["--gas-price", "36000000000", "--data-gas-price", "1", "--timeout", "320", "--seed", "0"]
CMD ["--gas-price", "36000000000", "--data-gas-price", "1", "--timeout", "320", "--seed", "0", "--lite-mode", "--gas-price-strk", "36000000000", "--data-gas-price-strk", "1"]
345 changes: 4 additions & 341 deletions lib/accounts.ts
Original file line number Diff line number Diff line change
@@ -1,99 +1,7 @@
import {
Abi,
Account,
AllowArray,
ArraySignatureType,
CairoOption,
CairoOptionVariant,
Call,
CallData,
Contract,
DeployAccountContractPayload,
DeployContractResponse,
GetTransactionReceiptResponse,
InvocationsSignerDetails,
InvokeFunctionResponse,
RPC,
RawCalldata,
Signature,
UniversalDetails,
V2InvocationsSignerDetails,
V3InvocationsSignerDetails,
hash,
num,
shortString,
stark,
transaction,
uint256,
} from "starknet";
import { Account, Call, CallData, RPC, uint256 } from "starknet";
import { manager } from "./manager";
import { ensureSuccess } from "./receipts";
import { LegacyArgentSigner, LegacyKeyPair, LegacyMultisigSigner, LegacyStarknetKeyPair } from "./signers/legacy";
import { ArgentSigner, KeyPair, RawSigner, randomStarknetKeyPair } from "./signers/signers";
import { ethAddress, strkAddress } from "./tokens";

export const VALID = BigInt(shortString.encodeShortString("VALID"));

export class ArgentAccount extends Account {
// Increase the gas limit by 30% to avoid failures due to gas estimation being too low with tx v3 and transactions the use escaping
override async deployAccount(
payload: DeployAccountContractPayload,
details?: UniversalDetails,
): Promise<DeployContractResponse> {
details ||= {};
if (!details.skipValidate) {
details.skipValidate = false;
}
return super.deployAccount(payload, details);
}

override async execute(
calls: AllowArray<Call>,
abis?: Abi[],
details: UniversalDetails = {},
): Promise<InvokeFunctionResponse> {
details ||= {};
if (!details.skipValidate) {
details.skipValidate = false;
}
if (details.resourceBounds) {
return super.execute(calls, abis, details);
}
const estimate = await this.estimateFee(calls, details);
return super.execute(calls, abis, {
...details,
resourceBounds: {
...estimate.resourceBounds,
l1_gas: {
...estimate.resourceBounds.l1_gas,
max_amount: num.toHexString(num.addPercent(estimate.resourceBounds.l1_gas.max_amount, 30)),
},
},
});
}
}

export interface ArgentWallet {
account: ArgentAccount;
accountContract: Contract;
owner: KeyPair;
}

export interface ArgentWalletWithGuardian extends ArgentWallet {
guardian: KeyPair;
}

export interface LegacyArgentWallet {
account: ArgentAccount;
accountContract: Contract;
owner: LegacyKeyPair;
guardian: LegacyKeyPair;
}

export interface ArgentWalletWithGuardianAndBackup extends ArgentWalletWithGuardian {
guardianBackup: KeyPair;
}

export const deployer = (() => {
if (manager.isDevnet) {
const devnetAddress = "0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691";
Expand All @@ -119,265 +27,20 @@ export const genericAccount = (() => {

export const deployerV3 = setDefaultTransactionVersionV3(deployer);

export function setDefaultTransactionVersion(account: ArgentAccount, newVersion: boolean): Account {
export function setDefaultTransactionVersion(account: Account, newVersion: boolean): Account {
const newDefaultVersion = newVersion ? RPC.ETransactionVersion.V3 : RPC.ETransactionVersion.V2;
if (account.transactionVersion === newDefaultVersion) {
return account;
}
return new ArgentAccount(account, account.address, account.signer, account.cairoVersion, newDefaultVersion);
return new Account(account, account.address, account.signer, account.cairoVersion, newDefaultVersion);
}

export function setDefaultTransactionVersionV3(account: ArgentAccount): ArgentAccount {
export function setDefaultTransactionVersionV3(account: Account): Account {
return setDefaultTransactionVersion(account, true);
}

console.log("Deployer:", deployer.address);

export async function deployOldAccount(
owner = new LegacyStarknetKeyPair(),
guardian = new LegacyStarknetKeyPair(),
salt = num.toHex(randomStarknetKeyPair().privateKey),
): Promise<LegacyArgentWallet> {
const proxyClassHash = await manager.declareFixtureContract("Proxy");
const oldArgentAccountClassHash = await manager.declareFixtureContract("OldArgentAccount");

const constructorCalldata = CallData.compile({
implementation: oldArgentAccountClassHash,
selector: hash.getSelectorFromName("initialize"),
calldata: CallData.compile({ owner: owner.publicKey, guardian: guardian.publicKey }),
});

const contractAddress = hash.calculateContractAddressFromHash(salt, proxyClassHash, constructorCalldata, 0);

const account = new Account(manager, contractAddress, owner);
account.signer = new LegacyMultisigSigner([owner, guardian]);

await fundAccount(account.address, 1e16, "ETH"); // 0.01 ETH

const { transaction_hash } = await account.deployAccount({
classHash: proxyClassHash,
constructorCalldata,
contractAddress,
addressSalt: salt,
});
await manager.waitForTransaction(transaction_hash);
const accountContract = await manager.loadContract(account.address);
accountContract.connect(account);
return { account, accountContract, owner, guardian };
}

async function deployAccountInner(params: DeployAccountParams): Promise<
DeployAccountParams & {
account: Account;
classHash: string;
owner: KeyPair;
guardian?: KeyPair;
salt: string;
transactionHash: string;
}
> {
const finalParams = {
...params,
classHash: params.classHash ?? (await manager.declareLocalContract("ArgentAccount")),
salt: params.salt ?? num.toHex(randomStarknetKeyPair().privateKey),
owner: params.owner ?? randomStarknetKeyPair(),
useTxV3: params.useTxV3 ?? false,
selfDeploy: params.selfDeploy ?? false,
};
const guardian = finalParams.guardian
? finalParams.guardian.signerAsOption
: new CairoOption(CairoOptionVariant.None);
const constructorCalldata = CallData.compile({ owner: finalParams.owner.signer, guardian });

const { classHash, salt } = finalParams;
const contractAddress = hash.calculateContractAddressFromHash(salt, classHash, constructorCalldata, 0);
const fundingCall = finalParams.useTxV3
? await fundAccountCall(contractAddress, finalParams.fundingAmount ?? 1e16, "STRK") // 0.01 STRK
: await fundAccountCall(contractAddress, finalParams.fundingAmount ?? 1e18, "ETH"); // 1 ETH
const calls = fundingCall ? [fundingCall] : [];

const transactionVersion = finalParams.useTxV3 ? RPC.ETransactionVersion.V3 : RPC.ETransactionVersion.V2;
const signer = new ArgentSigner(finalParams.owner, finalParams.guardian);
const account = new ArgentAccount(manager, contractAddress, signer, "1", transactionVersion);

let transactionHash;
if (finalParams.selfDeploy) {
const response = await deployer.execute(calls);
await manager.waitForTransaction(response.transaction_hash);
const { transaction_hash } = await account.deploySelf({ classHash, constructorCalldata, addressSalt: salt });
transactionHash = transaction_hash;
} else {
const udcCalls = deployer.buildUDCContractPayload({ classHash, salt, constructorCalldata, unique: false });
const { transaction_hash } = await deployer.execute([...calls, ...udcCalls]);
transactionHash = transaction_hash;
}

await manager.waitForTransaction(transactionHash);
return { ...finalParams, account, transactionHash };
}

export type DeployAccountParams = {
useTxV3?: boolean;
classHash?: string;
owner?: KeyPair;
guardian?: KeyPair;
salt?: string;
fundingAmount?: number | bigint;
selfDeploy?: boolean;
};

export async function deployAccount(
params: DeployAccountParams = {},
): Promise<ArgentWalletWithGuardian & { transactionHash: string }> {
params.guardian ||= randomStarknetKeyPair();
const { account, owner, transactionHash } = await deployAccountInner(params);
const accountContract = await manager.loadContract(account.address);
accountContract.connect(account);
return { account, accountContract, owner, guardian: params.guardian, transactionHash };
}

export async function deployAccountWithoutGuardian(
params: Omit<DeployAccountParams, "guardian"> = {},
): Promise<ArgentWallet & { transactionHash: string }> {
const { account, owner, transactionHash } = await deployAccountInner(params);
const accountContract = await manager.loadContract(account.address);
accountContract.connect(account);
return { account, accountContract, owner, transactionHash };
}

export async function deployAccountWithGuardianBackup(
params: DeployAccountParams & { guardianBackup?: KeyPair } = {},
): Promise<ArgentWalletWithGuardianAndBackup> {
const guardianBackup = params.guardianBackup ?? randomStarknetKeyPair();

const wallet = (await deployAccount(params)) as ArgentWalletWithGuardianAndBackup & { transactionHash: string };
await wallet.accountContract.change_guardian_backup(guardianBackup.compiledSignerAsOption);

wallet.account.signer = new ArgentSigner(wallet.owner, guardianBackup);
wallet.guardianBackup = guardianBackup;
wallet.accountContract.connect(wallet.account);
return wallet;
}

export async function deployLegacyAccount(classHash: string) {
const owner = new LegacyStarknetKeyPair();
const guardian = new LegacyStarknetKeyPair();
const salt = num.toHex(owner.privateKey);
const constructorCalldata = CallData.compile({ owner: owner.publicKey, guardian: guardian.publicKey });
const contractAddress = hash.calculateContractAddressFromHash(salt, classHash, constructorCalldata, 0);
await fundAccount(contractAddress, 1e15, "ETH"); // 0.001 ETH
const account = new Account(manager, contractAddress, owner, "1");
account.signer = new LegacyArgentSigner(owner, guardian);

const { transaction_hash } = await account.deploySelf({
classHash,
constructorCalldata,
addressSalt: salt,
});
await manager.waitForTransaction(transaction_hash);

const accountContract = await manager.loadContract(account.address);
accountContract.connect(account);
return { account, accountContract, owner, guardian };
}

export async function upgradeAccount(
accountToUpgrade: Account,
newClassHash: string,
calldata: RawCalldata = [],
): Promise<GetTransactionReceiptResponse> {
const { transaction_hash } = await accountToUpgrade.execute(
{
contractAddress: accountToUpgrade.address,
entrypoint: "upgrade",
calldata: CallData.compile({ implementation: newClassHash, calldata }),
},
undefined,
{ maxFee: 1e14 },
);
return await ensureSuccess(await manager.waitForTransaction(transaction_hash));
}

export async function executeWithCustomSig(
account: ArgentAccount,
transactions: AllowArray<Call>,
signature: ArraySignatureType,
transactionsDetail: UniversalDetails = {},
): Promise<InvokeFunctionResponse> {
const signer = new (class extends RawSigner {
public async signRaw(messageHash: string): Promise<string[]> {
return signature;
}
})();
const newAccount = new ArgentAccount(
manager,
account.address,
signer,
account.cairoVersion,
account.transactionVersion,
);

return await newAccount.execute(transactions, undefined, transactionsDetail);
}

export async function getSignerDetails(account: ArgentAccount, calls: Call[]): Promise<InvocationsSignerDetails> {
const newAccount = new ArgentAccount(
manager,
account.address,
account.signer,
account.cairoVersion,
account.transactionVersion,
);
const customSigner = new (class extends RawSigner {
public signerDetails?: InvocationsSignerDetails;
public async signTransaction(calls: Call[], signerDetails: InvocationsSignerDetails): Promise<Signature> {
this.signerDetails = signerDetails;
throw Error("Should not execute");
}
public async signRaw(messageHash: string): Promise<string[]> {
throw Error("Not implemented");
}
})();
newAccount.signer = customSigner;
try {
await newAccount.execute(calls, undefined);
throw Error("Should not execute");
} catch (customError) {
return customSigner.signerDetails!;
}
}

export function calculateTransactionHash(transactionDetail: InvocationsSignerDetails, calls: Call[]): string {
const compiledCalldata = transaction.getExecuteCalldata(calls, transactionDetail.cairoVersion);
let transactionHash;
if (Object.values(RPC.ETransactionVersion2).includes(transactionDetail.version as any)) {
const transactionDetailV2 = transactionDetail as V2InvocationsSignerDetails;
transactionHash = hash.calculateInvokeTransactionHash({
...transactionDetailV2,
senderAddress: transactionDetailV2.walletAddress,
compiledCalldata,
});
} else if (Object.values(RPC.ETransactionVersion3).includes(transactionDetail.version as any)) {
const transactionDetailV3 = transactionDetail as V3InvocationsSignerDetails;
transactionHash = hash.calculateInvokeTransactionHash({
...transactionDetailV3,
senderAddress: transactionDetailV3.walletAddress,
compiledCalldata,
nonceDataAvailabilityMode: stark.intDAM(transactionDetailV3.nonceDataAvailabilityMode),
feeDataAvailabilityMode: stark.intDAM(transactionDetailV3.feeDataAvailabilityMode),
});
} else {
throw Error("unsupported transaction version");
}
return transactionHash;
}

export async function fundAccount(recipient: string, amount: number | bigint, token: "ETH" | "STRK") {
const call = await fundAccountCall(recipient, amount, token);
const response = await deployer.execute(call ? [call] : []);
await manager.waitForTransaction(response.transaction_hash);
}

export async function fundAccountCall(
recipient: string,
amount: number | bigint,
Expand Down
Loading
Loading