{"content":{"title":"starknet智能合约编写、部署和调用-基于cairo2.3.0","body":"# 前言\r\nOpenZeppelin、Hadhat 和 Foundry都在支持和更新starknet，这是积极信号，尽快学起来\r\nstarknet智能合约使用cairo语言编写，最近刚刚更新2.3.0\r\ncairo编程语言类rust，建议搭配学，或者先学rust更好理解\r\n***rust编程语言会在未来几年快速流行（仅个人理解）***\r\n# 本教程环境\r\nwindows 10\r\nmac和linux请自行配置\r\nvscode\r\n下载cairo1插件（可省略）\r\n\r\ncairo 2.3.0\r\nhttps://github.com/starkware-libs/cairo/tree/main\r\nhttps://book.cairo-lang.org/zh-cn/title-page.html\r\nscarb 2.3.0\r\nhttps://github.com/software-mansion/scarb\r\nfoundry-rs 0.9.0\r\nhttps://github.com/foundry-rs/starknet-foundry\r\nhttps://foundry-rs.github.io/starknet-foundry/index.html\r\nOpenZeppelin v0.8.0-beta.0\r\nhttps://github.com/OpenZeppelin/cairo-contracts/tree/main\r\n**目前oz稳定版本是v0.7.0，难用且不支持cairo2.3.0**\r\nv0.8.0-beta.0主要支持了**Components**\r\ncomponents提供了一种乐高组合合约代码的方式\r\n教程https://book.cairo-lang.org/zh-cn/ch99-01-05-00-components.html\r\n\r\n区块链浏览器\r\nhttps://testnet.starkscan.co/\r\n\r\n\r\n\r\n\r\n# 配置环境\r\n下载scarb 2.3 \r\n下载foundry-rs 0.9.0\r\nbin加入环境变量，并验证\r\n```\r\nscarb --version\r\n```\r\n```\r\nsnforge --version\r\n```\r\n# 创建项目\r\n```\r\nsnforge init hello_starknet\r\n```\r\n\r\n![1698466995981.png](https://img.learnblockchain.cn/attachments/2023/10/HojvzDcp653c8cb584731.png)\r\n\r\n# 项目配置\r\n\r\n\r\nScarb.toml配置如下：\r\n\r\n**url替换为测试网络rpc**\r\n\r\n\r\n```[package]\r\nname = \"hello_starknet\"\r\nversion = \"0.1.0\"\r\n\r\n# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html\r\n\r\n[dependencies]\r\nsnforge_std = { git = \"https://github.com/foundry-rs/starknet-foundry\", tag = \"v0.9.0\" }\r\nopenzeppelin = { git = \"https://github.com/OpenZeppelin/cairo-contracts.git\", tag = \"v0.8.0-beta.0\" }\r\nstarknet = \"2.3.0\"\r\n\r\n[[target.starknet-contract]]\r\ncasm = true\r\nHelloStarknet = { path = \"src/lib\" }\r\n# foo = { path = \"vendor/foo\" }\r\n\r\n[tool.sncast]\r\naccount = \"test_user_0\"\r\naccounts-file = \"accounts.json\"\r\n# url = \"http://127.0.0.1:5050/rpc\"\r\nurl = \"https://starknet-goerli.g.alchemy.com/v2/66iTEsBmPgN-------\"\r\n```\r\n\r\n# 初始化账户\r\n创建两个账号\r\n\r\n```\r\nsncast account create -n test_user_0 \r\n\r\nsncast account create -n test_user_1\r\n```\r\n根目录下会创建accounts.json文件，打开可以看到两个密钥信息（这是我的，你的肯定不一样）\r\n\r\ntest_user_0：0x3180b441f524c2de2515094fc85ffef278d9764f6ebbb87db68928c094177ff\r\ntest_user_1：0xdc64f74f50383703de669eb9dd4b7eb7f6ecbc792f34232112193c6ed86d9a\r\n\r\n**starknet网络转测试eth到这两个账户地址上（否则不能部署账户合约）**\r\n\r\neth到账后可部署两个账户合约到starknet网络\r\ntest_user_0和test_user_1为accounts.json中账户名\r\n\r\n```\r\nsncast account deploy --name test_user_0 --max-fee 8646000000000\r\nsncast account deploy --name test_user_1 --max-fee 8646000000000\r\n```\r\n\r\n# 编写合约\r\n拷贝如下代码到lib.cairo\r\n\r\n```#[starknet::interface]\r\ntrait IHelloStarknet<TContractState> {\r\n    fn increase_balance(ref self: TContractState, amount: felt252);\r\n    fn get_balance(self: @TContractState) -> felt252;\r\n}\r\n\r\n#[starknet::contract]\r\nmod HelloStarknet {\r\n    use starknet::ContractAddress;\r\n    use openzeppelin::access::ownable::Ownable;\r\n    \r\n    component!(path: Ownable, storage: Ownable_owner, event: OwnershipTransferred);\r\n\r\n    #[abi(embed_v0)]\r\n    impl OwnableImpl = Ownable::OwnableImpl<ContractState>;\r\n\r\n    impl OwnableInternalImpl = Ownable::InternalImpl<ContractState>;\r\n\r\n    #[storage]\r\n    struct Storage {\r\n        balance: felt252, \r\n        #[substorage(v0)]\r\n        Ownable_owner: Ownable::Storage\r\n    }\r\n\r\n    #[event]\r\n    #[derive(Drop, starknet::Event)]\r\n    enum Event {\r\n        OwnershipTransferred: Ownable::Event\r\n    }\r\n\r\n    #[constructor]\r\n    fn constructor(ref self: ContractState, owner: ContractAddress) {\r\n        self.Ownable_owner.initializer(owner);\r\n    }\r\n\r\n    #[external(v0)]\r\n    impl HelloStarknetImpl of super::IHelloStarknet<ContractState> {\r\n        fn increase_balance(ref self: ContractState, amount: felt252) {\r\n            assert(amount != 0, 'Amount cannot be 0');\r\n            self.Ownable_owner.assert_only_owner();\r\n            self.balance.write(self.balance.read() + amount);\r\n        }\r\n\r\n        fn get_balance(self: @ContractState) -> felt252 {\r\n            self.balance.read()\r\n        }\r\n    }\r\n}\r\n\r\n```\r\n简单解释：\r\nfoundry示例代码基础上，配合OZ中Ownerable实现简单权限控制\r\nincrease_balance方法只有owner地址可调用\r\nconstructor构造中初始化owner\r\n\r\n# 部署合约\r\n\r\ndeclare合约，如正确会输出class-hash\r\nHelloStarknet为Scarb.toml中定义\r\n\r\n```\r\nsncast declare -c HelloStarknet\r\n```\r\n> command: declare\r\nclass_hash: 0x689fe8545ecf92cc86ffea0350c9093e0c7e4c215cda2ee156b0202ba344c40\r\ntransaction_hash: 0x3a67707e712033bdbd32a24f8167bd37eea2224e3fcde41b89142f64de87a6f\r\n\r\ndeploy合约\r\n--constructor-calldata 为构造参数，配置test_user_0为合约owner\r\n\r\n```\r\nsncast deploy --class-hash 0x689fe8545ecf92cc86ffea0350c9093e0c7e4c215cda2ee156b0202ba344c40 --constructor-calldata 0x3180b441f524c2de2515094fc85ffef278d9764f6ebbb87db68928c094177ff\r\n```\r\n> command: deploy\r\ncontract_address: 0x454bf4e91e12423c7c279f44c0b1c6f569c2c2635584cb554a62a4c0b2a24e2\r\ntransaction_hash: 0x5575459f0905b4f7c0915cee21632a850d018051c392af51116f029ccedde50\r\n\r\n稍后可以在区块链浏览器查看合约地址\r\n\r\n# 调用合约\r\n## 查询 call\r\n查询合约owner\r\n\r\n```\r\nsncast call -a 0x454bf4e91e12423c7c279f44c0b1c6f569c2c2635584cb554a62a4c0b2a24e2 -f \"owner\"\r\n```\r\n> command: call\r\nresponse: [0x3180b441f524c2de2515094fc85ffef278d9764f6ebbb87db68928c094177ff]\r\n\r\n查询balance值，初始值应为0\r\n\r\n```\r\nsncast call -a 0x454bf4e91e12423c7c279f44c0b1c6f569c2c2635584cb554a62a4c0b2a24e2 -f \"get_balance\"\r\n```\r\n> command: call\r\nresponse: [0x0]\r\n\r\n## 交易 invoke\r\n**test_user_0调用increase_balance方法**\r\n\r\n```\r\nsncast -a test_user_0 invoke -a 0x454bf4e91e12423c7c279f44c0b1c6f569c2c2635584cb554a62a4c0b2a24e2 -f \"increase_balance\" -c 2\r\n```\r\n> command: invoke\r\ntransaction_hash: 0x2f79663fcc0eb7792c09377087b0cdbc5f069ef911232b62f51e0b1007b9668\r\n\r\n再次调用查询get_balance方法，值应为2\r\n> sncast call -a 0x454bf4e91e12423c7c279f44c0b1c6f569c2c2635584cb554a62a4c0b2a24e2 -f \"get_balance\"\r\ncommand: call\r\nresponse: [0x2]\r\n\r\n**test_user_1调用increase_balance方法**\r\n\r\n\r\n```\r\nsncast -a test_user_1 invoke -a 0x454bf4e91e12423c7c279f44c0b1c6f569c2c2635584cb554a62a4c0b2a24e2 -f \"increase_balance\" -c 2\r\n```\r\n> command: invoke\r\nerror: Transaction reverted: Error in the called contract (0x00dc64f74f50383703de669eb9dd4b7eb7f6ecbc792f34232112193c6ed86d9a):\r\nError at pc=0:81:\r\nGot an exception while executing a hint: Hint Error: Execution failed. Failure reason: \"Caller is not the owner\".\r\nCairo traceback (most recent call last):\r\nUnknown location (pc=0:731)\r\nUnknown location (pc=0:677)\r\nUnknown location (pc=0:291)\r\nUnknown location (pc=0:314)\r\n\r\n非owner，调用失败\r\n\r\n# 注\r\n***到目前，本教程代码在remix starknet插件中不能编译和运行，期待remix插件升级***"},"author":{"user":"https://learnblockchain.cn/people/2217","address":null},"history":null,"timestamp":1698478700,"version":1}