From ac364eb3f047971357bf77d013726725a634a00d Mon Sep 17 00:00:00 2001 From: Quantum3Labs <157618676+0xquantum3labs@users.noreply.github.com> Date: Fri, 26 Apr 2024 19:59:42 +0800 Subject: [PATCH] sync main (#31) --- .../nextjs/contracts/predeployedContracts.ts | 461 ++++++++++++++++++ .../nextjs/utils/scaffold-stark/contract.ts | 80 ++- .../snfoundry/contracts/src/challenge0.cairo | 82 +++- packages/snfoundry/scripts_js/deploy.js | 29 +- 4 files changed, 593 insertions(+), 59 deletions(-) create mode 100644 packages/nextjs/contracts/predeployedContracts.ts diff --git a/packages/nextjs/contracts/predeployedContracts.ts b/packages/nextjs/contracts/predeployedContracts.ts new file mode 100644 index 00000000..42d60d27 --- /dev/null +++ b/packages/nextjs/contracts/predeployedContracts.ts @@ -0,0 +1,461 @@ +/** + * This file is autogenerated by Scaffold-Stark. + * You should not edit it manually or your changes might be overwritten. + */ +const universalEthAddress = + "0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7"; + +const preDeployedContracts = { + devnet: { + Eth: { + address: universalEthAddress, + abi: [ + { + type: "impl", + name: "ERC20Impl", + interface_name: "openzeppelin::token::erc20::interface::IERC20", + }, + { + name: "openzeppelin::token::erc20::interface::IERC20", + type: "interface", + items: [ + { + name: "name", + type: "function", + inputs: [], + outputs: [ + { + type: "core::felt252", + }, + ], + state_mutability: "view", + }, + { + name: "symbol", + type: "function", + inputs: [], + outputs: [ + { + type: "core::felt252", + }, + ], + state_mutability: "view", + }, + { + name: "decimals", + type: "function", + inputs: [], + outputs: [ + { + type: "core::integer::u8", + }, + ], + state_mutability: "view", + }, + { + name: "total_supply", + type: "function", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "balance_of", + type: "function", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "allowance", + type: "function", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "spender", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "transfer", + type: "function", + inputs: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "amount", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "external", + }, + { + name: "transfer_from", + type: "function", + inputs: [ + { + name: "sender", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "amount", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "external", + }, + { + name: "approve", + type: "function", + inputs: [ + { + name: "spender", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "amount", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "external", + }, + ], + }, + { + name: "ERC20CamelOnlyImpl", + type: "impl", + interface_name: + "openzeppelin::token::erc20::interface::IERC20CamelOnly", + }, + { + type: "interface", + name: "openzeppelin::token::erc20::interface::IERC20CamelOnly", + items: [ + { + name: "totalSupply", + type: "function", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "balanceOf", + type: "function", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "transferFrom", + type: "function", + inputs: [ + { + name: "sender", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "amount", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "external", + }, + ], + }, + ], + }, + }, + sepolia: { + Eth: { + address: universalEthAddress, + abi: [ + { + type: "impl", + name: "ERC20Impl", + interface_name: "openzeppelin::token::erc20::interface::IERC20", + }, + { + name: "openzeppelin::token::erc20::interface::IERC20", + type: "interface", + items: [ + { + name: "name", + type: "function", + inputs: [], + outputs: [ + { + type: "core::felt252", + }, + ], + state_mutability: "view", + }, + { + name: "symbol", + type: "function", + inputs: [], + outputs: [ + { + type: "core::felt252", + }, + ], + state_mutability: "view", + }, + { + name: "decimals", + type: "function", + inputs: [], + outputs: [ + { + type: "core::integer::u8", + }, + ], + state_mutability: "view", + }, + { + name: "total_supply", + type: "function", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "balance_of", + type: "function", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "allowance", + type: "function", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "spender", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "transfer", + type: "function", + inputs: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "amount", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "external", + }, + { + name: "transfer_from", + type: "function", + inputs: [ + { + name: "sender", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "amount", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "external", + }, + { + name: "approve", + type: "function", + inputs: [ + { + name: "spender", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "amount", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "external", + }, + ], + }, + { + name: "ERC20CamelOnlyImpl", + type: "impl", + interface_name: + "openzeppelin::token::erc20::interface::IERC20CamelOnly", + }, + { + type: "interface", + name: "openzeppelin::token::erc20::interface::IERC20CamelOnly", + items: [ + { + name: "totalSupply", + type: "function", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "balanceOf", + type: "function", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + name: "transferFrom", + type: "function", + inputs: [ + { + name: "sender", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "amount", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "external", + }, + ], + }, + ], + }, + }, +} as const; + +export default preDeployedContracts; diff --git a/packages/nextjs/utils/scaffold-stark/contract.ts b/packages/nextjs/utils/scaffold-stark/contract.ts index b24cf165..c6e663b1 100644 --- a/packages/nextjs/utils/scaffold-stark/contract.ts +++ b/packages/nextjs/utils/scaffold-stark/contract.ts @@ -1,6 +1,6 @@ import scaffoldConfig from "~~/scaffold.config"; import deployedContractsData from "~~/contracts/deployedContracts"; -// import externalContractsData from "~~/contracts/externalContracts"; +import predeployedContracts from "~~/contracts/predeployedContracts"; import type { ExtractAbiFunction, FunctionArgs, @@ -15,6 +15,13 @@ import { import { Address } from "@starknet-react/chains"; import { uint256 } from "starknet"; import { byteArray } from "starknet-dev"; +import type { MergeDeepRecord } from "type-fest/source/merge-deep"; + +type AddExternalFlag = { + [network in keyof T]: { + [ContractName in keyof T[network]]: T[network][ContractName]; + }; +}; type ConfiguredChainId = (typeof scaffoldConfig)["targetNetworks"][0]["network"]; @@ -24,13 +31,6 @@ type Contracts = ContractsDeclaration[ConfiguredChainId]; export type ContractName = keyof Contracts; export type Contract = Contracts[TContractName]; -type AddExternalFlag = { - [ChainId in keyof T]: { - [ContractName in keyof T[ChainId]]: T[ChainId][ContractName] & { - external?: true; - }; - }; -}; export enum ContractCodeStatus { "LOADING", "DEPLOYED", @@ -47,40 +47,38 @@ export type GenericContractsDeclaration = { }; }; -// const deepMergeContracts = < -// L extends Record, -// E extends Record -// >( -// local: L, -// external: E -// ) => { -// const result: Record = {}; -// const allKeys = Array.from( -// new Set([...Object.keys(external), ...Object.keys(local)]) -// ); -// for (const key of allKeys) { -// if (!external[key]) { -// result[key] = local[key]; -// continue; -// } -// const amendedExternal = Object.fromEntries( -// Object.entries( -// external[key] as Record> -// ).map(([contractName, declaration]) => [ -// contractName, -// { ...declaration, external: true }, -// ]) -// ); -// result[key] = { ...local[key], ...amendedExternal }; -// } -// return result as MergeDeepRecord< -// AddExternalFlag, -// AddExternalFlag, -// { arrayMergeMode: "replace" } -// >; -// }; +const deepMergeContracts = < + L extends Record, + E extends Record, +>( + local: L, + external: E, +) => { + const result: Record = {}; + const allKeys = Array.from( + new Set([...Object.keys(local), ...Object.keys(external)]), + ); + for (const key of allKeys) { + if (!external[key]) { + result[key] = local[key]; + continue; + } + const amendedExternal = Object.fromEntries( + Object.entries(external[key] as Record>), + ); + result[key] = { ...local[key], ...amendedExternal }; + } + return result as MergeDeepRecord< + AddExternalFlag, + AddExternalFlag, + { arrayMergeMode: "spread" } + >; +}; -const contractsData = deployedContractsData; +const contractsData = deepMergeContracts( + deployedContractsData, + predeployedContracts, +); type IsContractDeclarationMissing = typeof contractsData extends { [key in ConfiguredChainId]: any; diff --git a/packages/snfoundry/contracts/src/challenge0.cairo b/packages/snfoundry/contracts/src/challenge0.cairo index 2fd45ca2..2a4f4932 100644 --- a/packages/snfoundry/contracts/src/challenge0.cairo +++ b/packages/snfoundry/contracts/src/challenge0.cairo @@ -26,14 +26,13 @@ mod Challenge0 { use openzeppelin::token::erc721::ERC721Component; use openzeppelin::access::ownable::OwnableComponent; use openzeppelin::token::erc721::interface::IERC721Metadata; + use openzeppelin::token::erc721::interface::IERC721; + use starknet::get_caller_address; component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); - - #[abi(embed_v0)] - impl ERC721Impl = ERC721Component::ERC721Impl; #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableImpl; @@ -119,6 +118,48 @@ mod Challenge0 { } } + #[abi(embed_v0)] + impl WrappedIERC721Impl of IERC721 { + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + self.erc721.balance_of(account) + } + fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { + self.erc721.owner_of(token_id) + } + fn safe_transfer_from( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ) { + self.erc721.safe_transfer_from(from, to, token_id, data) + } + // Override transfer_from to use the internal fn _before_token_transfer() + fn transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + self._transfer_from(from, to, token_id) + } + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { + self.erc721.approve(to, token_id) + } + fn set_approval_for_all( + ref self: ContractState, operator: ContractAddress, approved: bool + ) { + self.erc721.set_approval_for_all(operator, approved) + } + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { + self.erc721.get_approved(token_id) + } + fn is_approved_for_all( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + self.erc721.is_approved_for_all(owner, operator) + } + } + + #[abi(embed_v0)] impl IERC721EnumerableImpl of IERC721Enumerable { fn token_of_owner_by_index( @@ -142,6 +183,38 @@ mod Challenge0 { self.erc721._mint(recipient, token_id); } + fn _transfer( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + assert(!to.is_zero(), ERC721Component::Errors::INVALID_RECEIVER); + let owner = self.erc721._owner_of(token_id); + assert(from == owner, ERC721Component::Errors::WRONG_SENDER); + + self._before_token_transfer(from, to, token_id, 1); + + assert(from == owner, ERC721Component::Errors::WRONG_SENDER); + + // Implicit clear approvals, no need to emit an event + self.erc721.ERC721_token_approvals.write(token_id, Zero::zero()); + + self.erc721.ERC721_balances.write(from, self.erc721.ERC721_balances.read(from) - 1); + self.erc721.ERC721_balances.write(to, self.erc721.ERC721_balances.read(to) + 1); + self.erc721.ERC721_owners.write(token_id, to); + + self.erc721.emit(ERC721Component::Transfer { from, to, token_id }); + } + + fn _transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + assert( + self.erc721._is_approved_or_owner(get_caller_address(), token_id), + ERC721Component::Errors::UNAUTHORIZED + ); + + self._transfer(from, to, token_id); + } + // ICounter internal functions fn _increment(ref self: ContractState) { self.counter.write(self.counter.read() + 1); @@ -222,7 +295,8 @@ mod Challenge0 { } else if (from != to) { self._remove_token_from_owner_enumeration(from, first_token_id); } - if (to == Zero::zero()) { //self._remove_token_from_all_tokens_enumeration(first_token_id); + if (to == Zero::zero()) { + self._remove_token_from_all_tokens_enumeration(first_token_id); } else if (to != from) { self._add_token_to_owner_enumeration(to, first_token_id); } diff --git a/packages/snfoundry/scripts_js/deploy.js b/packages/snfoundry/scripts_js/deploy.js index 3166066e..15a25666 100644 --- a/packages/snfoundry/scripts_js/deploy.js +++ b/packages/snfoundry/scripts_js/deploy.js @@ -11,21 +11,25 @@ const deployScript = async () => { // }, // "SimpleStorage" // ); + // await deployContract( + // { + // owner: deployer.address, // the deployer address is the owner of the contract + // }, + // "Challenge0" + // ); + const values = ({ + classHash: helloStarknetClassHash, + abi: helloStarknetAbi, + address: ContractAddress, + } = await deployContract(null, "ExampleExternalContract")); await deployContract( { - owner: deployer.address, // the deployer address is the owner of the contract + external_contract_address: values.address, + eth_contract_address: + "0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7", }, - "Challenge0" + "Challenge1" ); - - // const values = { - // classHash: helloStarknetClassHash, - // abi: helloStarknetAbi, - // address: ContractAddress, - // } = await deployContract(null, "ExampleExternalContract"); - // await deployContract({ external_contract_address: values.address, eth_contract_address: "0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7" } - // , "Challenge1"); - //await deployContract(null, "TransferETH"); // await deployContract( // { @@ -37,7 +41,6 @@ const deployScript = async () => { // }, // "PresetERC20" // ); - // await deployContract( // { // base_uri: "https://example.com/", @@ -48,7 +51,6 @@ const deployScript = async () => { // }, // "PresetERC1155" // ); - // await deployContract( // { // public_key: @@ -56,7 +58,6 @@ const deployScript = async () => { // }, // "Challenge3" // ); - // await deployContract( // { // voter_1: