{"author":{"address":"0xf54D741dF49d8976e52519f77a02327c77c2aDcd","user":"https://learnblockchain.cn/people/22222"},"content":{"body":"# 1.需求: 使用sui move 实现购买股票，根据股票分红\r\n\r\nsui 支持把钱转给一个对象地址， 提供一种机制，将对象地址拥有的钱，提取到这个对象的owner的钱包。\r\n\r\n如下图,\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/12/BSRwmCqz674eebb0b9a18.png)\r\n\r\n1 2 : 用户A 发布合约,拥有分红权限\r\n\r\n3 4 :用户B 花钱购买了股票 ,合约里面增加了钱\r\n\r\n5 : 将股票自由转移给C,不需要通知用户A\r\n\r\n6 :用户A 分红,将分红发给股票, 这个例子里面分红20%\r\n\r\n----这里可能经过了很多运营,合约里面的钱增值了, 本文不涉及相关逻辑\r\n\r\n7 :用户C 拿着股票,到合约来领取分红到钱包\r\n\r\n![image-20241203190809685](images/image-20241203190809685.png)\r\n\r\n# 2. 样例代码\r\n\r\n```tsx\r\n#[allow(lint(self_transfer),unused_use)]\r\nmodule book::stock_market\r\n{\r\n    use sui::coin::{Self,Coin};\r\n    use sui::sui::SUI;\r\n    use sui::balance::{Self,Balance};\r\n    use  sui::table::{Table,Self};\r\n    use sui::transfer::{Receiving};\r\n\t//\r\n    public struct Share  has store {\r\n        id : address,\r\n        amount : u64\r\n    }\r\n\r\n    public struct Shares has key {\r\n        id : UID,\r\n        count : u32,\r\n        holders : Table\u003cu32, Share \u003e ,\r\n        balances : Balance\u003cSUI\u003e,\r\n        supply   : u64,\r\n    \r\n    }\r\n\r\n    //控制权限，分红需要此权限\r\n    public struct AdminCap has key{\r\n        id : UID,\r\n    }\r\n    public struct Stock has key,store{\r\n        id : UID,\r\n        amount : u64,\r\n    }\r\n    fun init( ctx : \u0026mut TxContext){\r\n        let shares = Shares{\r\n            id : object::new(ctx),\r\n            count : 0,\r\n            holders : table::new\u003cu32,Share\u003e(ctx),\r\n            balances : balance::zero\u003cSUI\u003e(),\r\n            supply : 0\r\n        };\r\n        transfer::share_object(shares);\r\n\r\n        let admin = AdminCap{\r\n            id : object::new(ctx)\r\n        };\r\n        transfer::transfer(admin,ctx.sender());\r\n    }\r\n\r\n    // 创建股票和份额\r\n    fun create_share(amount :u64 , ctx : \u0026mut TxContext) : (Stock,Share){\r\n        let stock_id = object::new(ctx);\r\n        let stock_addr = stock_id.uid_to_address();\r\n        let stock= Stock{\r\n            id : stock_id,\r\n            amount  : amount\r\n        };\r\n        //记录股票地址，方便给股票地址转账\r\n        let share = Share{\r\n            amount : amount,\r\n            id : stock_addr,\r\n        };\r\n        return (stock,share)\r\n    }\r\n\r\n    //记录份额\r\n    fun add_share(shares : \u0026mut Shares,share : Share){\r\n        \r\n        shares.supply = shares.supply + share.amount;\r\n        table::add(\u0026mut shares.holders,shares.count,share);\r\n        shares.count = shares.count + 1;\r\n    }\r\n\r\n    // 花钱购买stock\r\n    entry fun buy_stock(shares: \u0026mut Shares,coin : Coin\u003cSUI\u003e, ctx : \u0026mut TxContext){\r\n        let value = coin::value(\u0026coin);\r\n        balance::join(\u0026mut shares.balances, coin.into_balance());\r\n        let (stock,share) = create_share(value,ctx);\r\n        add_share(shares, share);\r\n        transfer::transfer(stock,ctx.sender());\r\n    }\r\n\r\n    //阶段性分红，percent： 百分比乘以10000， 根据份额，把一部分钱发给股票\r\n    public fun allocate(_cap : \u0026AdminCap, \r\n                        shares :\u0026mut  Shares,\r\n                        percent : u64, \r\n                        ctx : \u0026mut TxContext){\r\n        let mut index :u32 = 0;\r\n        let supply = shares.supply;\r\n        while(index \u003c shares.count){\r\n            let share = sui::table::borrow(\u0026shares.holders,index);\r\n            let value = share.amount * shares.balances.value() *  percent /(10000) / supply;\r\n            if(value \u003e 0){\r\n                let coin = coin::from_balance( balance::split(\u0026mut shares.balances,value),ctx);\r\n                let coin_id = object::id_to_address(\u0026object::id(\u0026coin));\r\n                //分红发到股票地址\r\n                transfer::public_transfer(coin, share.id);\r\n            };\r\n            index = index + 1;\r\n        }\r\n    }\r\n   // 将股票拥有的钱提取到股票拥有者的钱包.只有拥有股票的人才能使用这个stock参数\r\n    entry fun receive_stock_share(stock: \u0026mut Stock, receiver : Receiving\u003cCoin\u003cSUI\u003e\u003e,ctx :\u0026 TxContext){\r\n        let coin = transfer::public_receive\u003cCoin\u003cSUI\u003e\u003e(\u0026mut stock.id ,receiver );\r\n        transfer::public_transfer(coin,ctx.sender());\r\n    }\r\n}\r\n```\r\n\r\n# 3. 测试\r\n\r\n## 3.1 定义三个用户,A,B,C\r\n\r\n export ADDR_A=0xf7ec2215e565b7a18d7b00e70fccda74b30c3ecceffb5857b1b3d2249e28e94f\r\n export ADDR_B=0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c\r\n export ADDR_C=0x540105A7D2F5F54A812C630F2996F1790ED0E60D1F9A870CE397F03E4CEC9B38\r\n\r\n## 3.2 用户A 发布程序包\r\n\r\n```bash\r\n sui client switch --address $ADDR_A\r\n sui  client publish\r\n```\r\n\r\n### 3.2.1 输出信息:\r\n\r\n```bash\r\n  ┌──                                                                                                       │\r\n│  │ ObjectID: 0xa041d96de7c3f9a6823c3f73203705ebe69a6ca376d4233c65604c04b2dcc5fe                            │\r\n│  │ Sender: 0xf7ec2215e565b7a18d7b00e70fccda74b30c3ecceffb5857b1b3d2249e28e94f                              │\r\n│  │ Owner: Shared( 51 )                                                                                     │\r\n│  │ ObjectType: 0x6c6b4cad96bf40d9be1ecb2954747924e49c7966208d453557980c26cbf4dccf::stock_market::Shares    │\r\n│  │ Version: 51                                                                                             │\r\n│  │ Digest: 4VMCG7NvCu7UPD45ZcmWa8mkpz84WsJzj3greNTt1V2t                                                    │\r\n│  └──                                                                                                       │\r\n│  ┌──                                                                                                       │\r\n│  │ ObjectID: 0xe40ab477a340442774f77668d4b6708ea366b195c25f9a9cf455eb94222c1b8b                            │\r\n│  │ Sender: 0xf7ec2215e565b7a18d7b00e70fccda74b30c3ecceffb5857b1b3d2249e28e94f                              │\r\n│  │ Owner: Account Address ( 0xf7ec2215e565b7a18d7b00e70fccda74b30c3ecceffb5857b1b3d2249e28e94f )           │\r\n│  │ ObjectType: 0x6c6b4cad96bf40d9be1ecb2954747924e49c7966208d453557980c26cbf4dccf::stock_market::AdminCap  │\r\n│  │ Version: 51                                                                                             │\r\n│  │ Digest: 9A6jXJfMgi87jfnJFJKFZT5PSfH8KRw5NKGKoGuHYi5C    \r\n```\r\n\r\n### 3.2.2 发布得到如下输出，配置相关包地址， adminCap地址， SHARES对象地址\r\n\r\n```bash\r\nexport SHARES=0xa041d96de7c3f9a6823c3f73203705ebe69a6ca376d4233c65604c04b2dcc5fe\r\nexport ADMIN=0xe40ab477a340442774f77668d4b6708ea366b195c25f9a9cf455eb94222c1b8b\r\nexport PKG=0x6c6b4cad96bf40d9be1ecb2954747924e49c7966208d453557980c26cbf4dccf\r\n```\r\n\r\n## 3.3  B用户，购买股票\r\n\r\n### 3.3.1 切换B用户\r\n\r\n```BASH\r\n  sui client switch --address $ADDR_B\r\n```\r\n\r\n### 3.3.2 获取gas ,devnet会自动发10个sui\r\n\r\n```bash\r\n  sui client faucet \r\n  # 查看当前的gas值\r\n  sui client gas\r\n\r\n\r\n╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮\r\n│ gasCoinId                                                          │ mistBalance (MIST) │ suiBalance (SUI) │\r\n├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤\r\n│ 0x729890d6387c699f2a586fffe1daf803f0cba18a6b3cc481d922df304f40fbe7 │ 10000000000        │ 10.00            │\r\n╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯\r\n```\r\n\r\n\r\n## 3.4  B用户购买股票buy stocket, STOCK =\u003eB\r\n\r\n从现有gas中切出5000000个mist ，用来购买股票\r\n\r\nsui client ptb --split-coins gas [5000000] \\\r\n--assign new_coin \\\r\n--move-call $PKG::stock_market::buy_stock  @$SHARES new_coin\r\n\r\n根据输出信息，可以获得STOCK\r\n`\r\nCreated Objects:                                                                                                                        │\r\n│  ┌──                                                                                                                                    │\r\n│  │ ObjectID: 0x16b4b76e57c92325dc44a47cf5734b4241f11ba25eb9360d77bdf8a0e2b8cbd8                                                         │\r\n│  │ Sender: 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c                                                           │\r\n│  │ Owner: Account Address ( 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c )                                        │\r\n│  │ ObjectType: 0x6c6b4cad96bf40d9be1ecb2954747924e49c7966208d453557980c26cbf4dccf::stock_market::Stock                                  │\r\n│  │ Version: 54                                                                                                                          │\r\n│  │ Digest: BfuEtZjb2TL6wrrsajwH7ZamgsnV71LB6W2mVHwkmjma                                                                                 │\r\n│  └──                                                        \r\n`\r\n\r\nexport STOCK=0x16b4b76e57c92325dc44a47cf5734b4241f11ba25eb9360d77bdf8a0e2b8cbd8\r\n\r\n## 3.5 股票自由转移，这里B将股票转移给C B-\u003eC : stock\r\n\r\n```bash\r\nsui client ptb --transfer-objects [@$STOCK]  @$ADDR_C\r\n```\r\n\r\n\r\n## 3.6 用户A 发起分红,分红20% ,将分红转移到股票地址\r\n\r\n\r\n```bash\r\nsui client switch --address $ADDR_A\r\nsui client ptb --move-call $PKG::stock_market::allocate @$ADMIN @$SHARES 2000\r\n```\r\n\r\n## 3.7  用户C 提取股票分红到钱包\r\n\r\n### 3.7.1 查看股票拥有的对象列表，可以发现股票已经拥有了coin:\r\n\r\n```bash\r\nsui client objects $STOCK\r\n```\r\n\r\n得到\r\n\r\n```\r\n╭───────────────────────────────────────────────────────────────────────────────────────╮\r\n│ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │\r\n│ │ objectId   │  0xc06d80746fd966134e89e527d24484afa647f60f919db31d373dcb9a292b046c  │ │\r\n│ │ version    │  55                                                                  │ │\r\n│ │ digest     │  ntJJXEPdJz0gE+8IqFXtHt7pOgf36Tapb0YV16cp+Gg=                        │ │\r\n│ │ objectType │  0x0000..0002::coin::Coin                                            │ │\r\n│ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │\r\n```\r\n\r\n根据输出的objectId,配置需要提取的COIN_ID\r\n\r\n ```bash\r\nexport COIN_ID=0xc06d80746fd966134e89e527d24484afa647f60f919db31d373dcb9a292b046c\r\n ```\r\n\r\n### 3.7.2 切换到用户C, 用户c 才拥有stock\r\n\r\n```bash \r\n sui client switch --address $ADDR_C\r\n```\r\n\r\n### 3.7.3 查看交易前的 C 的余额\r\n\r\n```\r\n sui client gas\r\n╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮\r\n│ gasCoinId                                                          │ mistBalance (MIST) │ suiBalance (SUI) │\r\n├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤\r\n│ 0x15789f7840c3d7758a024545376a8f6ee61e8162b3593ac8f3ae9a7f7b499200 │ 9996916552         │ 9.99             │\r\n╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯\r\n```\r\n\r\n### 3.7.4 执行交易,获取股票分红, 股票的拥有者C ,  coin.owner = stock  =\u003e coin.owner == ADDR_C\r\n\r\n```bash\r\nsui client ptb --move-call $PKG::stock_market::receive_stock_share @$STOCK @$COIN_ID\r\n```\r\n\r\n### 3.7.5 查看交易后的余额\r\n\r\n```bash\r\nsui client gas\r\n╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮\r\n│ gasCoinId                                                          │ mistBalance (MIST) │ suiBalance (SUI) │\r\n├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤\r\n│ 0x15789f7840c3d7758a024545376a8f6ee61e8162b3593ac8f3ae9a7f7b499200 │ 9995882960         │ 9.99             │\r\n│ 0xc06d80746fd966134e89e527d24484afa647f60f919db31d373dcb9a292b046c │ 1000000            │ 0.00             │\r\n╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯\r\n```\r\n\r\n\r\n\r\n# 附录","title":"sui move实现股票分红的一种实现"},"history":null,"timestamp":1733225550,"version":1}