Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/devnet-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ jobs:

- name: Setup dev chain
run: |
make run-madara-ci MADARA_MODE=devnet
make run-devnet-ci MADARA_MODE=devnet
2 changes: 1 addition & 1 deletion .github/workflows/devnet-mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ jobs:

- name: Setup dev chain
run: |
make run-madara-ci MADARA_MODE=devnet BASE_PATH=/tmp/db
make run-devnet-ci MADARA_MODE=devnet BASE_PATH=/tmp/db
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ appchain:
@echo "Waiting for Bootstrapper L2 container to finish..."
@docker wait bootstrapper_l2

# Run madara test
test:
@echo "Running madara tests..."
@cd deps/scripts/madara_tests && npm install && npm test

# Run the transfer scripts
transfer:
@echo "Running transfer scripts..."
Expand All @@ -43,6 +48,8 @@ stop-madara:
stop-appchain:
@cd deps && docker compose down

run-devnet-ci: build madara test stop-madara

run-madara-ci: build madara stop-madara

run-appchain-transfer-ci: build appchain transfer stop-appchain
3 changes: 3 additions & 0 deletions deps/scripts/madara_tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib
package-lock.json
node_modules
262 changes: 262 additions & 0 deletions deps/scripts/madara_tests/basic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
import { RpcProvider, Account, Contract, CallData, cairo } from "starknet";
import { RPC_URL, SIGNER_PRIVATE, SIGNER_CONTRACT_ADDRESS } from "./constant";
import {
readContractSierra,
readContractCasm,
readContractSierraInArtifacts,
} from "./utils";
import { hash, stark, ec } from "starknet";

/**
* Test order configuration, ideally the tests should be able to
* run in parallel but given in the current case if we have 2 txns
* from the same account, it throws an error of having same txn hash
* because the nonce is not getting updated for the first txn because
* the block was not produced yet.
*
* TODO: Remove this when we have a way to get the updated nonce
*/
const testOrder = [
{ name: "Declare Contract", fn: declareContract },
{ name: "Deploy Contract", fn: deployContract },
{ name: "Transfer Funds", fn: transferFunds },
{ name: "Deploy Account", fn: deployAccount },
];

/**
* Test context interface
*/
interface TestContext {
provider: RpcProvider;
account: Account;
}

describe("Starknet Contract Tests", () => {
let provider: RpcProvider;
let account: Account;
let nonce: number;

beforeAll(async () => {
// Initialize provider and account
provider = new RpcProvider({ nodeUrl: RPC_URL });
account = new Account(provider, SIGNER_CONTRACT_ADDRESS, SIGNER_PRIVATE);
});

// Run tests in specified order
testOrder.forEach(({ name, fn }) => {
test(name, async () => {
const result = await fn({ provider, account });

// TODO: this is a workaround to wait for the block to be produced
// given we have block time of 3 seconds, we wait for 5 seconds so that
// nonce gets updated. Remove this when we have a way to get the updated nonce
// when there is a txn already in the pool from same account
await new Promise((resolve) => setTimeout(resolve, 5000));
});
});
});

/**
* Declares a contract on the Starknet network
*
* @param {TestContext} context - The test context
* @returns {Promise<{classHash: string}>} The declared class hash
*/
async function declareContract({ provider, account }: TestContext) {
// Read the Sierra and CASM representations of the HelloStarknet contract
const sierra = readContractSierra("madara_contracts_HelloStarknet");
const casm = readContractCasm("madara_contracts_HelloStarknet");

// Declare the contract on the network
const declareResponse = await account.declare({
contract: sierra,
casm: casm,
});

// Wait for the declaration transaction to be confirmed
await provider.waitForTransaction(declareResponse.transaction_hash);

// Add assertion to check if the class hash is valid
expect(declareResponse.class_hash).toBeTruthy();

// Retrieve the declared class from the network
let response = await provider.getClass(declareResponse.class_hash);

// Verify the retrieved class matches the declared contract
if ("sierra_program" in response) {
expect(response.sierra_program).toEqual(sierra.sierra_program);
expect(response.abi).toEqual(sierra.abi);
expect(response.contract_class_version).toEqual(
sierra.contract_class_version,
);
expect(response.entry_points_by_type).toEqual(sierra.entry_points_by_type);
} else {
throw new Error("UnExpected CompiledSierra");
}
}

/**
* Deploys a contract to the Starknet network
*
* @param {TestContext} context - The test context
*/
async function deployContract({ provider, account }: TestContext) {
// Read the Sierra representation of the HelloStarknet contract
const sierra = readContractSierra("madara_contracts_HelloStarknet");
// Compute the class hash of the contract
let classHash = hash.computeContractClassHash(sierra);

// Deploy the contract
const deployResult = await account.deploy({
classHash: classHash,
});

// Wait for the deployment transaction to be confirmed
await provider.waitForTransaction(deployResult.transaction_hash);

// Add assertion to check if the contract address is valid
expect(deployResult.contract_address[0]).toBeTruthy();

// Retrieve the class hash for the deployed contract
let response = await provider.getClassHashAt(
deployResult.contract_address[0],
);

// Verify that the retrieved class hash matches the computed class hash
expect(response).toEqual(classHash);
}

/**
* Deploys an account contract to the Starknet network
*
* @param {TestContext} context - The test context
*/
/**
* Deploys an account contract to the Starknet network
*
* @param {TestContext} context - The test context containing provider, account, and nonce
*/
async function deployAccount({ provider, account }: TestContext) {
// Read the Sierra contract class for the account
const sierra = readContractSierraInArtifacts(
"openzeppelin_AccountUpgradeable",
);

// Compute the class hash of the account contract
let classHash = hash.computeContractClassHash(sierra);

// Generate a new random private key for the account
const privateKey = stark.randomAddress();

// Derive the public key from the private key
const publicKey = ec.starkCurve.getStarkKey(privateKey);

// Prepare the constructor calldata with the public key
const calldata = { publicKey: publicKey };

// Calculate the future address of the account contract
const accountAddress = hash.calculateContractAddressFromHash(
publicKey,
classHash,
calldata,
0,
);

// Create a new Account instance with the calculated address and private key
const newAccount = new Account(provider, accountAddress, privateKey);

// Deploy the account contract
const { transaction_hash, contract_address } = await newAccount.deployAccount(
{
classHash: classHash,
constructorCalldata: calldata,
addressSalt: publicKey,
},
{
maxFee: 0,
},
);

// Wait for the transaction to be confirmed and get the receipt
let transactionReceipt = await provider.waitForTransaction(transaction_hash);

// Retrieve the class hash for the deployed account contract
let response = await provider.getClassHashAt(contract_address);

// Verify that the deployed contract's class hash matches the expected class hash
expect(response).toEqual(classHash);
}

/**
* Transfers funds between accounts using an ERC20 token contract
*
* @param {TestContext} context - The test context
*/
async function transferFunds({ provider, account }: TestContext) {
// Constants for the transfer operation
// Note: These addresses are specific to the devnet environment
const RECEIVER_ADDRESS =
"0x5e9e93c6235f8ae6c2f4f0069bd30753ec21b26fbad80cfbf5da2c1bc573d69";
const TRANSFER_AMOUNT = 100000000000n;
const ERC20_CONTRACT_ADDRESS =
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7";

// Read the ERC20 contract class
const erc20ContractData = readContractSierraInArtifacts(
"openzeppelin_ERC20Upgradeable",
);

// Create an instance of the ERC20 contract
const erc20Instance = new Contract(
erc20ContractData.abi,
ERC20_CONTRACT_ADDRESS,
provider,
);

// Connect the account to the ERC20 contract instance
erc20Instance.connect(account);

// Get the initial balances of sender and receiver
const preTransactSenderBalance = await erc20Instance.balance_of(
SIGNER_CONTRACT_ADDRESS,
);
const preTransactReceiverBalance =
await erc20Instance.balance_of(RECEIVER_ADDRESS);

// Execute the transfer
// Note: We are setting maxFee to zero here
const transferResponse = await account.execute(
{
contractAddress: ERC20_CONTRACT_ADDRESS,
entrypoint: "transfer",
calldata: CallData.compile({
recipient: RECEIVER_ADDRESS,
amount: cairo.uint256(TRANSFER_AMOUNT),
}),
},
{
maxFee: 0,
},
);

// Wait for the transfer transaction to be confirmed
await provider.waitForTransaction(transferResponse.transaction_hash);

// Get the final balances of sender and receiver
const postTransactSenderBalance = await erc20Instance.balance_of(
SIGNER_CONTRACT_ADDRESS,
);
const postTransactReceiverBalance =
await erc20Instance.balance_of(RECEIVER_ADDRESS);

// Verify that the balances have been updated correctly
// Note: In real world case, the sender balance would be
// preTransactionSenderBalance - TRANSFER_AMOUNT - Fees
// but we had fees set to zero while executing transaction
expect(postTransactSenderBalance).toBe(
preTransactSenderBalance - TRANSFER_AMOUNT,
);
expect(postTransactReceiverBalance).toBe(
preTransactReceiverBalance + TRANSFER_AMOUNT,
);
}
5 changes: 5 additions & 0 deletions deps/scripts/madara_tests/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const RPC_URL = "http://127.0.0.1:9944";
export const SIGNER_CONTRACT_ADDRESS =
"0x055be462e718c4166d656d11f89e341115b8bc82389c3762a10eade04fcb225d";
export const SIGNER_PRIVATE =
"0x077e56c6dc32d40a67f6f7e6625c8dc5e570abe49c0a24e9202e4ae906abcc07";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":1,"contracts":[{"id":"30tdsko16k5am","package_name":"madara_contracts","contract_name":"TestContract","module_path":"madara_contracts::test_account::TestContract","artifacts":{"sierra":"madara_contracts_TestContract.contract_class.json","casm":"madara_contracts_TestContract.compiled_contract_class.json"}},{"id":"kp7flgguc0c3g","package_name":"madara_contracts","contract_name":"HelloStarknet","module_path":"madara_contracts::hello::HelloStarknet","artifacts":{"sierra":"madara_contracts_HelloStarknet.contract_class.json","casm":"madara_contracts_HelloStarknet.compiled_contract_class.json"}}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"prime":"0x800000000000011000000000000000000000000000000000000000000000001","compiler_version":"2.8.2","bytecode":["0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0xae","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0xa","0x482680017ffc8000","0x1","0x480a7ffd7fff8000","0x480680017fff8000","0x0","0x480280007ffc8000","0x10780017fff7fff","0x8","0x480a7ffc7fff8000","0x480a7ffd7fff8000","0x480680017fff8000","0x1","0x480680017fff8000","0x0","0x20680017fff7ffe","0x86","0x48307ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x10","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ff77fff8000","0x48127ff57fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x102","0x482480017fff8000","0x101","0x480080007fff8000","0xa0680017fff8000","0x9","0x4824800180007ff3","0x3de0","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff27fff","0x10780017fff7fff","0x56","0x4824800180007ff3","0x3de0","0x400080007ff37fff","0x482480017ff38000","0x1","0x20680017fff7ff7","0x10","0x40780017fff7fff","0x9","0x40780017fff7fff","0x1","0x480680017fff8000","0x416d6f756e742063616e6e6f742062652030","0x400080007ffe7fff","0x48127ff37fff8000","0x480a7ffb7fff8000","0x48127ffc7fff8000","0x482480017ffb8000","0x1","0x10780017fff7fff","0x39","0x480680017fff8000","0x0","0x480680017fff8000","0x206f38f7e4f15e87567361213c28f235cccdaa1d7fd34c9db1dfe9489c6a091","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffd","0x400280037ffb7ffe","0x480280057ffb8000","0x20680017fff7fff","0x25","0x480280067ffb8000","0x480280047ffb8000","0x480680017fff8000","0x0","0x480680017fff8000","0x206f38f7e4f15e87567361213c28f235cccdaa1d7fd34c9db1dfe9489c6a091","0x48307fef7ffc8000","0x480680017fff8000","0x53746f726167655772697465","0x400280077ffb7fff","0x400280087ffb7ffb","0x400280097ffb7ffc","0x4002800a7ffb7ffd","0x4002800b7ffb7ffe","0x4802800d7ffb8000","0x20680017fff7fff","0xd","0x40780017fff7fff","0x1","0x48127ff37fff8000","0x4802800c7ffb8000","0x482680017ffb8000","0xe","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x4802800c7ffb8000","0x482680017ffb8000","0x10","0x4802800e7ffb8000","0x4802800f7ffb8000","0x10780017fff7fff","0x9","0x40780017fff7fff","0x7","0x480280047ffb8000","0x482680017ffb8000","0x8","0x480280067ffb8000","0x480280077ffb8000","0x48127ff07fff8000","0x48127ffb7fff8000","0x48127ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff08000","0x1","0x48127fee7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4661696c656420746f20646573657269616c697a6520706172616d202331","0x400080007ffe7fff","0x48127ff87fff8000","0x48127ff67fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x60","0x4825800180007ffa","0x0","0x400280007ff97fff","0x482680017ff98000","0x1","0x48297ffc80007ffd","0x20680017fff7fff","0x4","0x10780017fff7fff","0x10","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ffa7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x55","0x482480017fff8000","0x54","0x480080007fff8000","0xa0680017fff8000","0x9","0x4824800180007ff8","0xd70","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff77fff","0x10780017fff7fff","0x2b","0x4824800180007ff8","0xd70","0x400080007ff87fff","0x480680017fff8000","0x0","0x480680017fff8000","0x206f38f7e4f15e87567361213c28f235cccdaa1d7fd34c9db1dfe9489c6a091","0x482480017ff68000","0x1","0x480680017fff8000","0x53746f7261676552656164","0x400280007ffb7fff","0x400280017ffb7ffb","0x400280027ffb7ffc","0x400280037ffb7ffd","0x480280057ffb8000","0x20680017fff7fff","0x10","0x40780017fff7fff","0x1","0x480280067ffb8000","0x400080007ffe7fff","0x48127ffb7fff8000","0x480280047ffb8000","0x482680017ffb8000","0x7","0x480680017fff8000","0x0","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x48127ffd7fff8000","0x480280047ffb8000","0x482680017ffb8000","0x8","0x480680017fff8000","0x1","0x480280067ffb8000","0x480280077ffb8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff58000","0x1","0x48127ff37fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe"],"bytecode_segment_lengths":[194,116],"hints":[[0,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[38,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[57,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x3de0"},"rhs":{"Deref":{"register":"AP","offset":-12}},"dst":{"register":"AP","offset":0}}}]],[75,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[97,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[114,[{"SystemCall":{"system":{"BinOp":{"op":"Add","a":{"register":"FP","offset":-5},"b":{"Immediate":"0x7"}}}}}]],[117,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[150,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[165,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[179,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[194,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[211,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[230,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0xd70"},"rhs":{"Deref":{"register":"AP","offset":-7}},"dst":{"register":"AP","offset":0}}}]],[254,[{"SystemCall":{"system":{"Deref":{"register":"FP","offset":-5}}}}]],[257,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[280,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[295,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]]],"entry_points_by_type":{"EXTERNAL":[{"selector":"0x362398bec32bc0ebb411203221a35a0301193a96f317ebe5e40be9f60d15320","offset":0,"builtins":["range_check"]},{"selector":"0x39e11d48192e4333233c7eb19d10ad67c362bb28580c604d67884c85da39695","offset":194,"builtins":["range_check"]}],"L1_HANDLER":[],"CONSTRUCTOR":[]}}
Loading