diff --git a/contracts/feralfile-exhibition-v4_2/feralfile.go b/contracts/feralfile-exhibition-v4_2/feralfile.go new file mode 100644 index 0000000..eba34a4 --- /dev/null +++ b/contracts/feralfile-exhibition-v4_2/feralfile.go @@ -0,0 +1,389 @@ +package feralfilev4_2 + +import ( + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math/big" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + + ethereum "github.com/bitmark-inc/account-vault-ethereum" + feralfilev4 "github.com/bitmark-inc/feralfile-exhibition-smart-contract/go-binding/feralfile-exhibition-v4_2" +) + +type FeralfileExhibitionV4_2Contract struct { + contractAddress string +} + +func FeralfileExhibitionV4_2ContractFactory(contractAddress string) ethereum.Contract { + return &FeralfileExhibitionV4_2Contract{ + contractAddress: contractAddress, + } +} + +// Deploy deploys the smart contract to ethereum blockchain +func (c *FeralfileExhibitionV4_2Contract) Deploy( + wallet *ethereum.Wallet, + arguments json.RawMessage) (string, string, error) { + var params struct { + Name string `json:"name"` + Symbol string `json:"symbol"` + Signer common.Address `json:"signer"` + Vault common.Address `json:"vault"` + CostReceiver common.Address `json:"cost_receiver"` + ContractURI string `json:"contract_uri"` + IsBurnable bool `json:"is_burnable"` + IsBridgeable bool `json:"is_bridgeable"` + SeriesIDs []*big.Int `json:"series_ids"` + SeriesMaxSupplies []*big.Int `json:"series_max_supplies"` + SeriesNextTokenIds []*big.Int `json:"series_next_token_ids"` + } + + if err := json.Unmarshal(arguments, ¶ms); err != nil { + return "", "", err + } + + t, err := wallet.Transactor() + if err != nil { + return "", "", err + } + + address, tx, _, err := feralfilev4.DeployFeralfileExhibitionV42( + t, + wallet.RPCClient(), + params.Name, + params.Symbol, + params.IsBurnable, + params.IsBridgeable, + params.Signer, + params.Vault, + params.CostReceiver, + params.ContractURI, + params.SeriesIDs, + params.SeriesMaxSupplies, + params.SeriesNextTokenIds, + ) + if err != nil { + return "", "", err + } + return address.String(), tx.Hash().String(), nil +} + +// Call is the entry function for account vault to interact with a smart contract. +func (c *FeralfileExhibitionV4_2Contract) Call( + wallet *ethereum.Wallet, + method, + fund string, + arguments json.RawMessage, + noSend bool, + gasLimit uint64, + gasPrice *int64, + nonce *uint64) (*types.Transaction, error) { + contractAddr := common.HexToAddress(c.contractAddress) + contract, err := feralfilev4.NewFeralfileExhibitionV42(contractAddr, wallet.RPCClient()) + if err != nil { + return nil, err + } + + t, err := wallet.Transactor() + if err != nil { + return nil, err + } + + t.NoSend = noSend + t.GasLimit = gasLimit + if gasPrice != nil && *gasPrice != 0 { + t.GasPrice = big.NewInt(*gasPrice * params.Wei) + } + + if nonce != nil { + t.Nonce = big.NewInt(int64(*nonce)) + } + + params, err := c.Parse(method, arguments) + if nil != err { + return nil, err + } + + switch method { + case "burnArtworks": + if len(params) != 1 { + return nil, errors.New("Invalid parameters") + } + + tokenIDs, ok := params[0].([]*big.Int) + if !ok { + return nil, errors.New("Invalid token burn parameters") + } + + return contract.BurnArtworks(t, tokenIDs) + case "mintArtworks": + if len(params) != 1 { + return nil, errors.New("Invalid parameters") + } + + mintData, ok := params[0].([]feralfilev4.FeralfileExhibitionV4MintData) + if !ok { + return nil, errors.New("Invalid token mint parameters") + } + + return contract.MintArtworks(t, mintData) + case "setTokenBaseURI": + if len(params) != 1 { + return nil, errors.New("Invalid parameters") + } + + baseURI, ok := params[0].(string) + if !ok { + return nil, errors.New("Invalid token base URI") + } + + return contract.SetTokenBaseURI(t, baseURI) + case "startSale": + return contract.StartSale(t) + case "transferOwnership": + if len(params) != 1 { + return nil, errors.New("Invalid parameters") + } + + newOwner, ok := params[0].(common.Address) + if !ok { + return nil, errors.New("Invalid new owner") + } + + return contract.TransferOwnership(t, newOwner) + case "buyBulkArtworks": + if len(params) != 4 { + return nil, errors.New("Invalid parameters") + } + + r, ok := params[0].([32]byte) + if !ok { + return nil, fmt.Errorf("invalid r") + } + + s, ok := params[1].([32]byte) + if !ok { + return nil, fmt.Errorf("invalid s") + } + + v, ok := params[2].(uint8) + if !ok { + return nil, fmt.Errorf("invalid v") + } + + saleData, ok := params[3].(feralfilev4.IFeralfileSaleDataV2SaleDataV2) + if !ok { + return nil, fmt.Errorf("invalid sale data") + } + + return contract.BuyBulkArtworks(t, r, s, v, saleData) + case "setApprovalForAll": + if len(params) != 2 { + return nil, errors.New("Invalid parameters") + } + + operator, ok := params[0].(common.Address) + if !ok { + return nil, errors.New("Invalid operator") + } + + approved, ok := params[1].(bool) + if !ok { + return nil, errors.New("Invalid approved") + } + + t.GasLimit = gasLimit + + return contract.SetApprovalForAll(t, operator, approved) + case "setAdvanceSetting": + if len(params) != 2 { + return nil, errors.New("Invalid parameters") + } + + return contract.SetAdvanceSetting(t, params[0].([]common.Address), params[1].([]*big.Int)) + default: + return nil, fmt.Errorf("unsupported method") + } +} + +func (c *FeralfileExhibitionV4_2Contract) Pack( + method string, + arguments json.RawMessage) ([]byte, error) { + abi, err := feralfilev4.FeralfileExhibitionV42MetaData.GetAbi() + if nil != err { + return nil, err + } + + parsedArgs, err := c.Parse(method, arguments) + if nil != err { + return nil, err + } + + return abi.Pack(method, parsedArgs...) +} + +func (c *FeralfileExhibitionV4_2Contract) Parse( + method string, + arguments json.RawMessage) ([]interface{}, error) { + switch method { + case "startSale": + return nil, nil + case "burnArtworks": + var params []ethereum.BigInt + if err := json.Unmarshal(arguments, ¶ms); err != nil { + return nil, err + } + if len(params) == 0 { + return nil, errors.New("Invalid token burn parameters") + } + + tokenIDs := make([]*big.Int, len(params)) + for i, v := range params { + tokenID := v.Int + tokenIDs[i] = &tokenID + } + + return []interface{}{tokenIDs}, nil + case "mintArtworks": + var params []struct { + SeriesID ethereum.BigInt `json:"series_id"` + TokenID ethereum.BigInt `json:"token_id"` + Owner common.Address `json:"owner"` + } + if err := json.Unmarshal(arguments, ¶ms); err != nil { + return nil, err + } + if len(params) == 0 { + return nil, errors.New("Invalid token mint parameters") + } + + mintData := make([]feralfilev4.FeralfileExhibitionV4MintData, len(params)) + for i := 0; i < len(params); i++ { + mintData[i] = feralfilev4.FeralfileExhibitionV4MintData{ + SeriesId: ¶ms[i].SeriesID.Int, + TokenId: ¶ms[i].TokenID.Int, + Owner: params[i].Owner, + } + } + + return []interface{}{mintData}, nil + case "setTokenBaseURI": + baseURI := strings.Trim(string(arguments), "\"") + if baseURI == "" { + return nil, errors.New("Invalid token base URI") + } + return []interface{}{baseURI}, nil + case "transferOwnership": + var newOwner common.Address + if err := json.Unmarshal(arguments, &newOwner); err != nil { + return nil, err + } + return []interface{}{newOwner}, nil + case "setApprovalForAll": + var params struct { + Operator common.Address `json:"operator"` + Approved bool `json:"approved"` + } + if err := json.Unmarshal(arguments, ¶ms); err != nil { + return nil, err + } + + return []interface{}{params.Operator, params.Approved}, nil + case "buyBulkArtworks": + var params struct { + SaleData struct { + Price ethereum.BigInt `json:"price"` + Cost ethereum.BigInt `json:"cost"` + ExpiryTime ethereum.BigInt `json:"expiryTime"` + Destination common.Address `json:"destination"` + Nonce ethereum.BigInt `json:"nonce"` + SeriesID ethereum.BigInt `json:"seriesId"` + Quantity uint16 `json:"quantity"` + RevenueShares []struct { + Recipient common.Address `json:"recipient"` + Bps ethereum.BigInt `json:"bps"` + } `json:"revenueShares"` + PayByVaultContract bool `json:"payByVaultContract"` + } `json:"saleData"` + R string `json:"r"` + S string `json:"s"` + V string `json:"v"` + } + + if err := json.Unmarshal(arguments, ¶ms); err != nil { + return nil, err + } + + rVal, err := hex.DecodeString(strings.Replace(params.R, "0x", "", -1)) + if err != nil { + return nil, err + } + sVal, err := hex.DecodeString(strings.Replace(params.S, "0x", "", -1)) + if err != nil { + return nil, err + } + vVal, err := strconv.ParseUint(strings.Replace(params.V, "0x", "", -1), 16, 64) + if err != nil { + return nil, err + } + if len(rVal) != 32 || len(sVal) != 32 { + return nil, errors.New("required signature length is 32") + } + var r32Val [32]byte + var s32Val [32]byte + copy(r32Val[:], rVal) + copy(s32Val[:], sVal) + + revenueShares := make([]feralfilev4.IFeralfileSaleDataRevenueShare, 0) + for _, v := range params.SaleData.RevenueShares { + bps := v.Bps.Int + revenueShares = append(revenueShares, feralfilev4.IFeralfileSaleDataRevenueShare{ + Recipient: v.Recipient, + Bps: &bps, + }) + } + + saleData := feralfilev4.IFeralfileSaleDataV2SaleDataV2{ + Price: ¶ms.SaleData.Price.Int, + Cost: ¶ms.SaleData.Cost.Int, + ExpiryTime: ¶ms.SaleData.ExpiryTime.Int, + Destination: params.SaleData.Destination, + Nonce: ¶ms.SaleData.Nonce.Int, + SeriesID: ¶ms.SaleData.SeriesID.Int, + Quantity: params.SaleData.Quantity, + RevenueShares: revenueShares, + PayByVaultContract: params.SaleData.PayByVaultContract, + } + + return []interface{}{r32Val, s32Val, uint8(vVal), saleData}, nil + case "setAdvanceSetting": + var params struct { + AdvanceAddresses []common.Address `json:"advance_addresses"` + AdvanceAmounts []ethereum.BigInt `json:"advance_amounts"` + } + if err := json.Unmarshal(arguments, ¶ms); err != nil { + return nil, err + } + + advanceAmounts := make([]*big.Int, len(params.AdvanceAmounts)) + for i, v := range params.AdvanceAmounts { + amount := v.Int // closure issue + advanceAmounts[i] = &amount + } + + return []interface{}{params.AdvanceAddresses, advanceAmounts}, nil + default: + return nil, fmt.Errorf("unsupported method") + } +} + +func init() { + ethereum.RegisterContract("FeralfileExhibitionV4_2", FeralfileExhibitionV4_2ContractFactory) +} diff --git a/go.mod b/go.mod index c5b18b2..408b457 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/bitmark-inc/account-vault-ethereum go 1.20 require ( - github.com/bitmark-inc/feralfile-exhibition-smart-contract/go-binding v0.0.0-20240420161158-7827629e2797 + github.com/bitmark-inc/feralfile-exhibition-smart-contract/go-binding v0.0.0-20240617045334-b5d26d3a3dec github.com/ethereum/go-ethereum v1.13.10 github.com/miguelmota/go-ethereum-hdwallet v0.1.1 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index f20b502..737545b 100644 --- a/go.sum +++ b/go.sum @@ -63,8 +63,8 @@ github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/bitmark-inc/feralfile-exhibition-smart-contract/go-binding v0.0.0-20240420161158-7827629e2797 h1:avNsve02bxwzXzHGbvxrd78MYSdRJmnVwj50HsqZQCc= -github.com/bitmark-inc/feralfile-exhibition-smart-contract/go-binding v0.0.0-20240420161158-7827629e2797/go.mod h1:shvtsuJyQJnByXKqf9n/jpjL4++FSJCFjXR/08iBnFY= +github.com/bitmark-inc/feralfile-exhibition-smart-contract/go-binding v0.0.0-20240617045334-b5d26d3a3dec h1:XlRCTNovfqBGhXDVqsuiCLtSoDF4qQszXRdjimicw04= +github.com/bitmark-inc/feralfile-exhibition-smart-contract/go-binding v0.0.0-20240617045334-b5d26d3a3dec/go.mod h1:shvtsuJyQJnByXKqf9n/jpjL4++FSJCFjXR/08iBnFY= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=