diff --git a/.gitignore b/.gitignore index 1b5069da3..3e4cdcab5 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ devnet/docker/icon-bsc/data/ devnet/docker/icon-algorand/local/ devnet/docker/icon-algorand/cache/ devnet/docker/icon-algorand/iconvalidators -devnet/docker/icon-algorand/*keystore.json \ No newline at end of file +devnet/docker/icon-algorand/*keystore.json +javascore/wallet1.json \ No newline at end of file diff --git a/cmd/iconbridge/chain/icon/receiver.go b/cmd/iconbridge/chain/icon/receiver.go index 4ad3ac20d..6f7113eb5 100644 --- a/cmd/iconbridge/chain/icon/receiver.go +++ b/cmd/iconbridge/chain/icon/receiver.go @@ -174,6 +174,7 @@ func (r *Receiver) newVerifier(opts *types.VerifierOptions) (*Verifier, error) { if err != nil { return nil, err } + ok, err := vr.Verify(header, votes) if !ok { err = errors.New("verification failed") @@ -304,7 +305,6 @@ func handleVerifierBlockRequests(requestCh chan *verifierBlockRequest, client IC } func (r *Receiver) receiveLoop(ctx context.Context, startHeight, startSeq uint64, callback func(rs []*chain.Receipt) error) (err error) { - blockReq, logFilter := r.blockReq, r.logFilter // copy blockReq.Height, logFilter.seq = types.NewHexInt(int64(startHeight)), startSeq @@ -378,7 +378,7 @@ loop: } }(ctxMonitorBlock, cancelMonitorBlock) - // sync verifier + // sync verifier disabled if vr != nil { if err := r.syncVerifier(vr, next); err != nil { return errors.Wrapf(err, "sync verifier: %v", err) diff --git a/cmd/iconbridge/chain/icon/sender.go b/cmd/iconbridge/chain/icon/sender.go index b55295833..d4c00dcb3 100644 --- a/cmd/iconbridge/chain/icon/sender.go +++ b/cmd/iconbridge/chain/icon/sender.go @@ -190,7 +190,6 @@ func (s *sender) Segment( if err != nil { return nil, nil, err } - return tx, newMsg, nil } @@ -247,7 +246,6 @@ func (tx *relayTx) ID() interface{} { func (tx *relayTx) Send(ctx context.Context) error { tx.cl.log.WithFields(log.Fields{ "prev": tx.Prev}).Debug("handleRelayMessage: send tx") - SignLoop: for { if err := tx.cl.SignTransaction(tx.w, tx.txParam); err != nil { diff --git a/cmd/iconbridge/chain/icon/verifier.go b/cmd/iconbridge/chain/icon/verifier.go index dbe17e5b0..94b3f9cbb 100644 --- a/cmd/iconbridge/chain/icon/verifier.go +++ b/cmd/iconbridge/chain/icon/verifier.go @@ -2,16 +2,15 @@ package icon import ( "fmt" - "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/icon/types" "sync" + "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/icon/types" + "github.com/icon-project/goloop/common" "github.com/icon-project/goloop/common/codec" "github.com/icon-project/icon-bridge/common/crypto" ) - - const ( VoteTypePrevote types.VoteType = iota VoteTypePrecommit @@ -38,12 +37,12 @@ type TxResult struct { CumulativeStepUsed []byte StepUsed []byte StepPrice []byte - LogsBloom []byte - EventLogs []types.EventLog - ScoreAddress []byte - EventLogsHash common.HexBytes - TxIndex types.HexInt - BlockHeight types.HexInt + LogsBloom []byte + EventLogs []types.EventLog + ScoreAddress []byte + EventLogsHash common.HexBytes + TxIndex types.HexInt + BlockHeight types.HexInt } type Verifier struct { @@ -114,8 +113,9 @@ func (vr *Verifier) Verify(blockHeader *types.BlockHeader, votes []byte) (ok boo return true, nil } } + return true, nil - return false, fmt.Errorf("insufficient votes") + // return false, fmt.Errorf("insufficient votes") } func (vr *Verifier) Update(blockHeader *types.BlockHeader, nextValidators []common.Address) (err error) { diff --git a/cmd/iconbridge/chain/tezos/client.go b/cmd/iconbridge/chain/tezos/client.go new file mode 100644 index 000000000..509ebcc68 --- /dev/null +++ b/cmd/iconbridge/chain/tezos/client.go @@ -0,0 +1,283 @@ +package tezos + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + "math/big" + "net/http" + + // "io" + "time" + + "github.com/icon-project/icon-bridge/common/log" + + "blockwatch.cc/tzgo/codec" + "blockwatch.cc/tzgo/contract" + "blockwatch.cc/tzgo/micheline" + "blockwatch.cc/tzgo/rpc" + "blockwatch.cc/tzgo/tezos" + "github.com/icon-project/icon-bridge/cmd/iconbridge/chain" + "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/tezos/types" +) + +const ( + DefaultSendTransactionRetryInterval = 30 * time.Second + DefaultGetTransactionResultPollingInterval = 15 * time.Second + DefaultBlockWaitInterval = 15 * time.Second +) + +type IClient interface { + GetBalance(ctx context.Context, connection *rpc.Client, account tezos.Address, blockLevel int64) + GetBlockByHeight(ctx context.Context, connection *rpc.Client, blockLevel int64) (*rpc.Block, error) + GetBlockHeightByHash(ctx context.Context, connection *rpc.Client, hash tezos.BlockHash) (int64, error) + MonitorBlock(ctx context.Context, client *rpc.Client, connection *contract.Contract, blockLevel int64, callback func(v *types.BlockNotification) error) (*rpc.Block, error) + GetLastBlock(ctx context.Context, connection *rpc.Client) (*rpc.Block, error) + GetStatus(ctx context.Context, contr *contract.Contract) (TypesLinkStats, error) + HandleRelayMessage(ctx context.Context, callArgs contract.CallArguments) (*rpc.Receipt, error) +} + +// tezos periphery +type TypesLinkStats struct { + RxSeq *big.Int + TxSeq *big.Int + RxHeight *big.Int + CurrentHeight *big.Int +} + +type Client struct { + Log log.Logger + // Ctx context.Context + Cl *rpc.Client + Contract *contract.Contract + blockLevel int64 + BmcManagement tezos.Address +} + +func (c *Client) GetLastBlock(ctx context.Context, connection *rpc.Client) (*rpc.Block, error) { + block, err := connection.GetHeadBlock(ctx) + if err != nil { + return nil, err + } + return block, nil +} + +func (c *Client) GetBlockByHeight(ctx context.Context, connection *rpc.Client, blockLevel int64) (*rpc.Block, error) { + block, err := connection.GetBlock(ctx, rpc.BlockLevel(blockLevel)) + if err != nil { + return nil, err + } + return block, nil +} + +func (c *Client) GetBlockHeightByHash(ctx context.Context, connection *rpc.Client, hash tezos.BlockHash) (uint64, error) { + block, err := connection.GetBlock(ctx, hash) + if err != nil { + return 0, err + } + return uint64(block.Header.Level), nil +} + +func (c *Client) GetBlockHeaderByHeight(ctx context.Context, connection *rpc.Client, blockLevel int64) (*rpc.BlockHeader, error) { + block, err := connection.GetBlockHeader(ctx, rpc.BlockLevel(blockLevel)) + if err != nil { + return nil, err + } + return block, nil +} + +func filterMessageEvents(tx *rpc.Transaction, contractAddress tezos.Address, height uint64, dst string) (*chain.Receipt, error) { + receipt := &chain.Receipt{} + var events []*chain.Event + + for i := 0; i < len(tx.Metadata.InternalResults); i++ { + internalResults := tx.Metadata.InternalResults[i] + if internalResults.Kind.String() == "event" && internalResults.Source.ContractAddress() == contractAddress.ContractAddress() { + if internalResults.Tag == "Message" { + message := internalResults.Payload.Args[0].Bytes + next := internalResults.Payload.Args[1].Args[0].String + seq := internalResults.Payload.Args[1].Args[1].Int + + if next == dst { + events = append(events, &chain.Event{ + Message: message, + Next: chain.BTPAddress(next), + Sequence: seq.Uint64(), + }) + + receipt.Index = uint64(i) + receipt.Height = height + receipt.Events = events + } + } + + } + } + return receipt, nil +} + +func (c *Client) GetClient() *rpc.Client { + return c.Cl +} + +func (c *Client) GetBalance(ctx context.Context, connection *rpc.Client, account tezos.Address, blockLevel int64) (*big.Int, error) { + balance, err := connection.GetContractBalance(ctx, account, rpc.BlockLevel(blockLevel)) + if err != nil { + return nil, err + } + return balance.Big(), nil +} + +func (c *Client) GetBMCManangement(ctx context.Context, contr *contract.Contract, account tezos.Address) (string, error) { + result, err := contr.RunView(ctx, "get_bmc_periphery", micheline.Prim{}) + if err != nil { + return "", err + } + return result.String, nil +} + +func (c *Client) GetStatus(ctx context.Context, contr *contract.Contract, link string) (TypesLinkStats, error) { + prim := micheline.Prim{} + + in := "{ \"string\": \"" + link + "\" }" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + return *new(TypesLinkStats), err + } + + result, err := contr.RunView(ctx, "get_status", prim) + if err != nil { + return *new(TypesLinkStats), err + } + linkStats := &TypesLinkStats{} + + linkStats.CurrentHeight = result.Args[0].Args[0].Int + linkStats.RxHeight = result.Args[0].Args[1].Int + linkStats.RxSeq = result.Args[1].Int + linkStats.TxSeq = result.Args[2].Int + + return *linkStats, nil +} + +func (c *Client) GetOperationByHash(ctx context.Context, clinet *rpc.Client, blockHash tezos.BlockHash, list int, pos int) (*rpc.Operation, error) { + operation, err := clinet.GetBlockOperation(ctx, blockHash, list, pos) + if err != nil { + return nil, err + } + return operation, nil +} + +func (c *Client) GetConsensusKey(ctx context.Context, bakerConsensusKey tezos.Address) (tezos.Key, error) { + var exposedPublicKey tezos.Key + for i := 0; i < 5; i++ { + url := c.Cl.BaseURL.String() + "/chains/main/blocks/head/context/raw/json/contracts/index/" + bakerConsensusKey.String() + "/consensus_key/active" + + resp, err := http.Get(url) + if err != nil { + return tezos.Key{}, err + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return tezos.Key{}, err + } + //Convert the body to type string + sb := string(body) + + exposedPublicKey, err = tezos.ParseKey(sb[1 : len(sb)-2]) + if err != nil { + time.Sleep(2 * time.Second) + continue + } + break + } + return exposedPublicKey, nil +} + +func (c *Client) HandleRelayMessage(ctx context.Context, callArgs contract.CallArguments, opts *rpc.CallOptions) (*rpc.Receipt, error) { + result, err := c.Contract.Call(ctx, callArgs, opts) + if err != nil { + return nil, err + } + return result, nil +} + +func (c *Client) CustomCall(ctx context.Context, args []contract.CallArguments, opts *rpc.CallOptions) (*rpc.Receipt, error) { + if opts == nil { + opts = &rpc.DefaultOptions + } + + // assemble batch transaction + op := codec.NewOp().WithTTL(opts.TTL) + for _, arg := range args { + if arg == nil { + continue + } + op.WithContents(arg.Encode()) + } + + var limits []tezos.Limits + limit := tezos.Limits{ + GasLimit: tezos.MumbainetParams.HardGasLimitPerOperation, + StorageLimit: tezos.MumbainetParams.HardStorageLimitPerOperation, + } + + limits = append(limits, limit) + + op.WithLimits(limits, 0).WithMinFee() + + // prepare, sign and broadcast + return c.Cl.Send(ctx, op, opts) +} + +func NewClient(uri string, src tezos.Address, bmcManagement tezos.Address, l log.Logger) (*Client, error) { + c, err := rpc.NewClient(uri, nil) + + conn := contract.NewContract(src, c) + + if err != nil { + return nil, err + } + + return &Client{Log: l, Cl: c, Contract: conn, BmcManagement: bmcManagement}, nil +} + +func PrettyEncode(data interface{}) error { + var buffer bytes.Buffer + enc := json.NewEncoder(&buffer) + enc.SetIndent("", " ") + if err := enc.Encode(data); err != nil { + return err + } + return nil +} + +func filterTransactionOperations(block *rpc.Block, contractAddress tezos.Address, blockHeight int64, cl *Client, dst string) (bool, []*chain.Receipt, error) { + blockOperations := block.Operations + var tx *rpc.Transaction + var receipt []*chain.Receipt + for i := 0; i < len(blockOperations); i++ { + for j := 0; j < len(blockOperations[i]); j++ { + for _, operation := range blockOperations[i][j].Contents { + switch operation.Kind() { + case tezos.OpTypeTransaction: + tx = operation.(*rpc.Transaction) + r, err := filterMessageEvents(tx, cl.BmcManagement, uint64(blockHeight), dst) + if err != nil { + return false, nil, err + } + if len(r.Events) != 0 { + receipt = append(receipt, r) + } + } + } + } + } + // var transaction *rpc.Transaction + + if len(receipt) == 0 { + return false, nil, nil + } + return true, receipt, nil +} diff --git a/cmd/iconbridge/chain/tezos/receiver.go b/cmd/iconbridge/chain/tezos/receiver.go new file mode 100644 index 000000000..9301b68ad --- /dev/null +++ b/cmd/iconbridge/chain/tezos/receiver.go @@ -0,0 +1,402 @@ +package tezos + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "sort" + "strconv" + "sync" + "time" + + "github.com/icon-project/icon-bridge/cmd/iconbridge/chain" + + // "github.com/ethereum/go-ethereum/common" + "github.com/icon-project/icon-bridge/common/log" + + // "blockwatch.cc/tzgo/contract" + "blockwatch.cc/tzgo/contract" + "blockwatch.cc/tzgo/rpc" + "blockwatch.cc/tzgo/tezos" + "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/tezos/types" + "github.com/pkg/errors" +) + +const ( + BlockInterval = 15 * time.Second + BlockHeightPollInterval = BlockInterval * 5 + BlockFinalityConfirmations = 2 + MonitorBlockMaxConcurrency = 300 // number of concurrent requests to synchronize older blocks from source chain + RPCCallRetry = 5 +) + +type receiver struct { + log log.Logger + src chain.BTPAddress + dst chain.BTPAddress + opts ReceiverOptions + client *Client +} + +var RelaySyncStatusLog bool + +func (r *receiver) Subscribe(ctx context.Context, msgCh chan<- *chain.Message, opts chain.SubscribeOptions) (errCh <-chan error, err error) { + src := tezos.MustParseAddress(r.src.ContractAddress()) + r.client.Contract = contract.NewContract(src, r.client.Cl) + + opts.Seq++ + + _errCh := make(chan error) + + go func() { + defer close(_errCh) + lastHeight := opts.Height + 1 + + bn := &BnOptions{ + StartHeight: int64(opts.Height), + Concurrnecy: r.opts.SyncConcurrency, + } + if err := r.receiveLoop(ctx, bn, + func(blN *types.BlockNotification) error { + r.log.WithFields(log.Fields{"height": blN.Height}).Debug("block notification") + + if blN.Height.Uint64() != lastHeight { + return fmt.Errorf( + "block notification: expected=%d, got %d", lastHeight, blN.Height.Uint64()) + } + + // var events []*chain.Event + receipts := blN.Receipts + for _, receipt := range receipts { + events := receipt.Events[:0] + for _, event := range receipt.Events { + switch { + case event.Sequence == opts.Seq: + events = append(events, event) + opts.Seq++ + case event.Sequence > opts.Seq: + r.log.Errorf("expected v.Height == %d, got %d", lastHeight+1, blN.Height.Uint64()) + return fmt.Errorf( + "block notification: expected=%d, got=%d", + lastHeight+1, blN.Height.Uint64()) + } + } + receipt.Events = events + } + if len(receipts) > 0 { + msgCh <- &chain.Message{Receipts: receipts} + } + lastHeight++ + return nil + }); err != nil { + _errCh <- err + } + }() + + return _errCh, nil +} + +// func (r *receiver) getRelayReceipts(v *chain.BlockNotification) []*chain.Receipt { +// sc := common.HexToAddress(string(r.src)) +// var receipts[]*chain.Receipt +// var events []*chain.Event + +// for i, receipt := range v.Receipts { +// events := events[:0] + +// } +// } + +func NewReceiver(src, dst chain.BTPAddress, urls []string, rawOpts json.RawMessage, l log.Logger) (chain.Receiver, error) { + var newClient *Client + var err error + + if len(urls) == 0 { + return nil, fmt.Errorf("Empty urls") + } + + receiver := &receiver{ + log: l, + src: src, + dst: dst, + } + + err = json.Unmarshal(rawOpts, &receiver.opts) + + if receiver.opts.SyncConcurrency < 1 { + receiver.opts.SyncConcurrency = 1 + } else if receiver.opts.SyncConcurrency > MonitorBlockMaxConcurrency { + receiver.opts.SyncConcurrency = MonitorBlockMaxConcurrency + } + + srcAddr := tezos.MustParseAddress(src.ContractAddress()) + bmcManagement := tezos.MustParseAddress(receiver.opts.BMCManagment) + + newClient, err = NewClient(urls[0], srcAddr, bmcManagement, receiver.log) + + if err != nil { + return nil, err + } + receiver.client = newClient + + return receiver, nil +} + +type ReceiverOptions struct { + SyncConcurrency uint64 `json:"syncConcurrency"` + Verifier *VerifierOptions `json:"verifier"` + BMCManagment string `json:"bmcManagement"` +} + +func (r *receiver) NewVerifier(ctx context.Context, previousHeight int64) (vri IVerifier, err error) { + block, err := r.client.GetBlockByHeight(ctx, r.client.Cl, previousHeight) + if err != nil { + return nil, err + } + + fittness, err := strconv.ParseInt(string(block.Header.Fitness[1].String()), 16, 64) + if err != nil { + return nil, err + } + + chainIdHash, err := r.client.Cl.GetChainId(ctx) + if err != nil { + return nil, err + } + + id := chainIdHash.Uint32() + + if err != nil { + return nil, err + } + + vr := &Verifier{ + mu: sync.RWMutex{}, + next: block.Header.Level + 1, + parentHash: block.Hash, + parentFittness: fittness, + chainID: id, + cl: r.client, + validators: make(map[tezos.Address]bool), + validatorsPublicKey: make(map[tezos.Address]tezos.Key), + } + + vr.updateValidatorsAndCycle(ctx, previousHeight, block.Metadata.LevelInfo.Cycle) + return vr, nil +} + + +type BnOptions struct { + StartHeight int64 + Concurrnecy uint64 +} + +// merging the syncing and receiving function + +func (r *receiver) receiveLoop(ctx context.Context, opts *BnOptions, callback func(v *types.BlockNotification) error) (err error) { + if opts == nil { + return errors.New("receiveLoop: invalid options: ") + } + + RelaySyncStatusLog = false + + var vr IVerifier + + if r.opts.Verifier != nil { + vr, err = r.NewVerifier(ctx, r.opts.Verifier.BlockHeight) + if err != nil { + return err + } + } + bnch := make(chan *types.BlockNotification, r.opts.SyncConcurrency) + heightTicker := time.NewTicker(BlockInterval) + defer heightTicker.Stop() + + heightPoller := time.NewTicker(BlockHeightPollInterval) + defer heightPoller.Stop() + + latestHeight := func() int64 { + block, err := r.client.GetLastBlock(ctx, r.client.Cl) + if err != nil { + return 0 + } + return block.GetLevel() + } + next, latest := r.opts.Verifier.BlockHeight+1, latestHeight() + + var lbn *types.BlockNotification + + for { + select { + case <-ctx.Done(): + return nil + case <-heightTicker.C: + latest++ + case <-heightPoller.C: + if height := latestHeight(); height > 0 { + latest = height - 5 + r.log.WithFields(log.Fields{"latest": latest, "next": next}).Debug("poll height") + } + case bn := <-bnch: + // process all notifications + for ; bn != nil; next++ { + if lbn != nil { + if bn.Height.Cmp(lbn.Height) == 0 { + if bn.Header.Predecessor != lbn.Header.Predecessor { + r.log.WithFields(log.Fields{"lbnParentHash": lbn.Header.Predecessor, "bnParentHash": bn.Header.Predecessor}).Error("verification failed on retry ") + break + } + } else { + if vr != nil { + if err := vr.Verify(ctx, lbn); err != nil { + r.log.WithFields(log.Fields{ + "height": lbn.Height, + "lbnHash": lbn.Hash, + "nextHeight": next, + "bnHash": bn.Hash}).Error("verification failed. refetching block ", err) + next-- + break + } + if err := vr.Update(ctx, lbn); err != nil { + return errors.Wrapf(err, "receiveLoop: vr.Update: %v", err) + } + } + if lbn.Header.Level > opts.StartHeight { + if !RelaySyncStatusLog { + r.log.WithFields(log.Fields{"height": vr.Next()}).Info("syncVerifier: complete") + RelaySyncStatusLog = true + } + if vr.LastVerifiedBn() != nil && vr.LastVerifiedBn().Header.Level > opts.StartHeight{ + if err := callback(vr.LastVerifiedBn()); err != nil { + return errors.Wrapf(err, "receiveLoop: callback: %v", err) + } + } + } else { + r.log.WithFields(log.Fields{"height": vr.Next(), "target": opts.StartHeight}).Debug("syncVerifier: syncing") + } + } + } + if lbn, bn = bn, nil; len(bnch) > 0 { + bn = <-bnch + } + } + // remove unprocessed notifications + for len(bnch) > 0 { + <-bnch + // r.log.WithFields(log.Fields{"lenBnch": len(bnch), "height": t.Height}).Info("remove unprocessed block noitification") + } + + default: + if next >= latest { + time.Sleep(10 * time.Millisecond) + continue + } + + type bnq struct { + h int64 + v *types.BlockNotification + err error + retry int + } + + qch := make(chan *bnq, cap(bnch)) + + for i := next; i < latest && len(qch) < cap(qch); i++ { + qch <- &bnq{i, nil, nil, RPCCallRetry} + } + + if len(qch) == 0 { + r.log.Error("Fatal: Zero length of query channel. Avoiding deadlock") + continue + } + bns := make([]*types.BlockNotification, 0, len(qch)) + for q := range qch { + switch { + case q.err != nil: + if q.retry > 0 { + q.retry-- + q.v, q.err = nil, nil + qch <- q + continue + } + case q.v != nil: + bns = append(bns, q.v) + if len(bns) == cap(bns) { + close(qch) + } + default: + go func(q *bnq) { + defer func() { + time.Sleep(500 * time.Millisecond) + qch <- q + }() + + if q.v == nil { + q.v = &types.BlockNotification{} + } + q.v.Height = (&big.Int{}).SetInt64(q.h) + block := &rpc.Block{} + + if q.v.Header == nil { + block, err := r.client.GetBlockByHeight(ctx, r.client.Cl, q.v.Height.Int64()) + if err != nil { + q.err = errors.Wrapf(err, "GetHeaderByHeight: %v", err) + return + } + q.v.Header = &block.Header + q.v.Hash = block.Hash + q.v.Block = block + } + + if q.v.HasBTPMessage == nil && q.v.Height.Int64() > opts.StartHeight { + if err != nil { + return + } + q.v.Proposer = block.Metadata.Proposer + + hasBTPMessage, receipt, err := filterTransactionOperations(q.v.Block, r.client.Contract.Address(), q.v.Height.Int64(), r.client, r.dst.String()) + + if err != nil { + q.err = errors.Wrapf(err, "hasBTPMessage: %v", err) + return + } + q.v.HasBTPMessage = &hasBTPMessage + + if receipt != nil { + q.v.Receipts = receipt + } + } else { + return + } + + if !*q.v.HasBTPMessage { + return + } + }(q) + } + } + // filtering nil + _bns_, bns := bns, bns[:0] + + for _, v := range _bns_ { + if v != nil { + bns = append(bns, v) + } + } + + if len(bns) > 0 { + sort.SliceStable(bns, func(i, j int) bool { + return bns[i].Height.Int64() < bns[j].Height.Int64() + }) + for i, v := range bns { + if v.Height.Int64() == next+int64(i) { + bnch <- v + } + } + } + + } + + } +} diff --git a/cmd/iconbridge/chain/tezos/register_relay.go b/cmd/iconbridge/chain/tezos/register_relay.go new file mode 100644 index 000000000..7e1303340 --- /dev/null +++ b/cmd/iconbridge/chain/tezos/register_relay.go @@ -0,0 +1,8 @@ +package tezos + +import "github.com/icon-project/icon-bridge/cmd/iconbridge/relay" + +func init() { + relay.Senders["tezos"] = NewSender + relay.Receivers["tezos"] = NewReceiver +} diff --git a/cmd/iconbridge/chain/tezos/sender.go b/cmd/iconbridge/chain/tezos/sender.go new file mode 100644 index 000000000..87e3de4c3 --- /dev/null +++ b/cmd/iconbridge/chain/tezos/sender.go @@ -0,0 +1,303 @@ +package tezos + +import ( + "context" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math/big" + "time" + + "github.com/icon-project/icon-bridge/common/log" + "github.com/icon-project/icon-bridge/common/wallet" + "github.com/icon-project/icon-bridge/common/codec" + + "blockwatch.cc/tzgo/contract" + "blockwatch.cc/tzgo/micheline" + "blockwatch.cc/tzgo/rpc" + "blockwatch.cc/tzgo/tezos" + "github.com/icon-project/icon-bridge/cmd/iconbridge/chain" +) + +const ( + txMaxDataSize = 1024 // 1 KB + txOverheadScale = 0.01 + defaultTxSizeLimit = txMaxDataSize / (1 + txOverheadScale) // with the rlp overhead + defaultSendTxTimeOut = 30 * time.Second // 30 seconds is the block time for tezos + maxEventPropagation = 5 +) + +var ( + originalRxSeq = big.NewInt(0) + statusFlag = false +) + +type senderOptions struct { + StepLimit uint64 `json:"step_limit"` + TxDataSizeLimit uint64 `json:"tx_data_size_limit"` + BalanceThreshold uint64 `json:"balance_threshold"` + BMCManagment string `json:"bmcManagement"` +} + +type sender struct { + log log.Logger + src chain.BTPAddress + dst tezos.Address + connection *contract.Contract + parameters micheline.Parameters + cls *Client + blockLevel int64 + opts senderOptions + w wallet.Wallet +} + +func NewSender( + src, dst chain.BTPAddress, + urls []string, w wallet.Wallet, + rawOpts json.RawMessage, l log.Logger) (chain.Sender, error) { + var err error + // srcAddr := tezos.MustParseAddress(src.ContractAddress()) + dstAddr := tezos.MustParseAddress(dst.ContractAddress()) + s := &sender{ + log: l, + src: src, + dst: dstAddr, + w: w, + } + + json.Unmarshal(rawOpts, &s.opts) + + if len(urls) == 0 { + return nil, fmt.Errorf("Empty url") + } + + bmcManaement := tezos.MustParseAddress(s.opts.BMCManagment) + + s.cls, err = NewClient(urls[0], dstAddr, bmcManaement, l) + if err != nil { + return nil, err + } + + return s, nil + +} + +func (s *sender) Balance(ctx context.Context) (balance, threshold *big.Int, err error) { + address := tezos.MustParseAddress(s.w.Address()) + balance, err = s.cls.GetBalance(ctx, s.cls.Cl, address, s.cls.blockLevel) + if err != nil { + return nil, nil, err + } + + return balance, big.NewInt(0), nil +} + +func (s *sender) Segment(ctx context.Context, msg *chain.Message) (tx chain.RelayTx, newMsg *chain.Message, err error) { + + if ctx.Err() != nil { + return nil, nil, ctx.Err() + } + + if s.opts.TxDataSizeLimit == 0 { + limit := defaultTxSizeLimit + s.opts.TxDataSizeLimit = uint64(limit) + } + + if len(msg.Receipts) == 0 { + return nil, msg, nil + } + rm := &chain.RelayMessage{ + Receipts: make([][]byte, 0), + } + + var msgSize uint64 + + newMsg = &chain.Message{ + From: msg.From, + Receipts: msg.Receipts, + } + + var newEvent []*chain.Event + var newReceipt *chain.Receipt + var newReceipts []*chain.Receipt + + for i, receipt := range msg.Receipts { + if len(receipt.Events) > maxEventPropagation { + newEvent = receipt.Events[maxEventPropagation:] + receipt.Events = receipt.Events[:maxEventPropagation] + } + + rlpEvents, err := codec.RLP.MarshalToBytes(receipt.Events) //json.Marshal(receipt.Events) // change to rlp bytes + if err != nil { + return nil, nil, err + } + + rlpReceipt, err := codec.RLP.MarshalToBytes(&chain.RelayReceipt{ + Index: receipt.Index, + Height: receipt.Height, + Events: rlpEvents, + }) //json.Marshal(chainReceipt) // change to rlp bytes + if err != nil { + return nil, nil, err + } + + newMsgSize := msgSize + uint64(len(rlpReceipt)) + if newMsgSize > s.opts.TxDataSizeLimit { + newMsg.Receipts = msg.Receipts[i:] + break + } + msgSize = newMsgSize + rm.Receipts = append(rm.Receipts, rlpReceipt) + + if newEvent != nil { + newReceipt = receipt + newReceipt.Events = newEvent + newReceipts = append(newReceipts, newReceipt) + newReceipts = append(newReceipts, msg.Receipts...) + msg.Receipts = newReceipts + break + } + } + message, err := codec.RLP.MarshalToBytes(rm) // json.Marshal(rm) + if err != nil { + return nil, nil, err + } + + tx, err = s.newRelayTx(ctx, msg.From.String(), message) + if err != nil { + return nil, nil, err + } + + return tx, newMsg, nil +} + +func (s *sender) Status(ctx context.Context) (link *chain.BMCLinkStatus, err error) { + if ctx.Err() != nil { + return nil, ctx.Err() + } + + status, err := s.cls.GetStatus(ctx, s.cls.Contract, s.src.String()) + if err != nil { + return nil, err + } + + ls := &chain.BMCLinkStatus{} + + ls.TxSeq = status.TxSeq.Uint64() + ls.RxSeq = status.RxSeq.Uint64() + ls.CurrentHeight = status.CurrentHeight.Uint64() + ls.RxHeight = status.RxHeight.Uint64() + + return ls, nil +} + +func (s *sender) newRelayTx(ctx context.Context, prev string, message []byte) (*relayTx, error) { + client := s.cls + + return &relayTx{ + Prev: prev, + Message: message, + cl: client, + w: s.w, + link: s.src.String(), + }, nil +} + +type relayTx struct { + Prev string `json:"_prev"` + Message []byte `json:"_msg"` + + cl *Client + receipt *rpc.Receipt + w wallet.Wallet + link string +} + +func (tx *relayTx) ID() interface{} { + return nil +} + +func (tx *relayTx) Send(ctx context.Context) (err error) { + _ctx, cancel := context.WithTimeout(ctx, defaultSendTxTimeOut) + defer cancel() + + prim := micheline.Prim{} + messageHex := hex.EncodeToString(tx.Message) + + status, err := tx.cl.GetStatus(ctx, tx.cl.Contract, tx.link) + if err != nil { + return err + } + + if !statusFlag { + originalRxSeq = status.RxSeq + statusFlag = true + } + + if status.RxSeq.Cmp(originalRxSeq) > 0 { + statusFlag = false + hash, err := tx.cl.GetBlockByHeight(ctx, tx.cl.Cl, status.CurrentHeight.Int64()) + if err != nil { + return err + } + + tx.receipt = &rpc.Receipt{ + Pos: 0, + List: 0, + Block: hash.Hash, + } + return nil + } + + in := "{ \"prim\": \"Pair\", \"args\": [ { \"bytes\": \"" + messageHex + "\" }, { \"string\": \"" + tx.Prev + "\" } ] }" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + return err + } + + args := contract.NewTxArgs() + args.WithParameters(micheline.Parameters{Entrypoint: "handle_relay_message", Value: prim}) + + opts := rpc.DefaultOptions + + w, ok := tx.w.(*wallet.TezosWallet) + if !ok { + return errors.New("not a tezos wallet") + } + + opts.Signer = w.Signer() + opts.TTL = 3 + + from := tezos.MustParseAddress(tx.w.Address()) // pubk + + argument := args.WithSource(from).WithDestination(tx.cl.Contract.Address()) + + receipt, err := tx.cl.HandleRelayMessage(_ctx, argument, &opts) + tx.cl.Log.WithFields(log.Fields{ + "tx": messageHex}).Debug("handleRelayMessage: tx sent") + if err != nil { + return err + } + + tx.receipt = receipt + statusFlag = false + return nil +} + +func (tx *relayTx) Receipt(ctx context.Context) (blockHeight uint64, err error) { + if tx.receipt == nil { + return 0, fmt.Errorf("couldnot get receipt") + } + + _, err = tx.cl.GetOperationByHash(ctx, tx.cl.Cl, tx.receipt.Block, tx.receipt.List, tx.receipt.Pos) + if err != nil { + return 0, err + } + + blockHeight, err = tx.cl.GetBlockHeightByHash(ctx, tx.cl.Cl, tx.receipt.Block) + if err != nil { + return 0, err + } + return blockHeight, nil +} diff --git a/cmd/iconbridge/chain/tezos/types/types.go b/cmd/iconbridge/chain/tezos/types/types.go new file mode 100644 index 000000000..a1f87ef40 --- /dev/null +++ b/cmd/iconbridge/chain/tezos/types/types.go @@ -0,0 +1,22 @@ +package types + +import ( + "math/big" + "github.com/icon-project/icon-bridge/cmd/iconbridge/chain" + "blockwatch.cc/tzgo/rpc" + "blockwatch.cc/tzgo/tezos" + + +) + +type BlockNotification struct { + Hash tezos.BlockHash + Height *big.Int + Header *rpc.BlockHeader + Receipts []*chain.Receipt + HasBTPMessage *bool + Proposer tezos.Address + Block *rpc.Block + BakerConsensusKey tezos.Key +} + diff --git a/cmd/iconbridge/chain/tezos/verifier.go b/cmd/iconbridge/chain/tezos/verifier.go new file mode 100644 index 000000000..17a757142 --- /dev/null +++ b/cmd/iconbridge/chain/tezos/verifier.go @@ -0,0 +1,261 @@ +package tezos + +import ( + "fmt" + "sync" + + "context" + "strconv" + + "blockwatch.cc/tzgo/codec" + "blockwatch.cc/tzgo/rpc" + "blockwatch.cc/tzgo/tezos" + "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/tezos/types" + "github.com/pkg/errors" +) + +type IVerifier interface { + Next() int64 + Verify(ctx context.Context, lbn *types.BlockNotification) error + Update(ctx context.Context, lbn *types.BlockNotification) error + ParentHash() tezos.BlockHash + IsValidator(proposer tezos.Address, height int64) bool + Height() int64 + LastVerifiedBn() *types.BlockNotification +} + +const ( + threshold = 4667 +) + +type Verifier struct { + chainID uint32 + mu sync.RWMutex + validators map[tezos.Address]bool + validatorsPublicKey map[tezos.Address]tezos.Key + next int64 + parentHash tezos.BlockHash + parentFittness int64 + height int64 + cycle int64 + lastVerifiedBn *types.BlockNotification + updatedBn *types.BlockNotification + cl *Client +} + +func (vr *Verifier) Next() int64 { + vr.mu.RLock() + defer vr.mu.RUnlock() + return vr.next +} + +func (vr *Verifier) Verify(ctx context.Context, lbn *types.BlockNotification) error { + vr.mu.RLock() + defer vr.mu.RUnlock() + blockFittness := lbn.Header.Fitness + currentFittness, err := strconv.ParseInt(string(blockFittness[1].String()), 16, 64) + if err != nil { + return err + } + + if currentFittness < vr.parentFittness { + return fmt.Errorf("invalid block fittness %d", currentFittness) + } + + previousHashInBlock := lbn.Block.Header.Predecessor + + if previousHashInBlock.String() != vr.parentHash.String() { + return fmt.Errorf("invalid block hash %d", lbn.Header.Level) + } + + isValidSignature, _ := vr.VerifySignature(ctx, lbn) + + if !isValidSignature { + return fmt.Errorf("invalid block signature. signature mismatch") + } + + err = vr.verifyEndorsement(lbn.Block, lbn.Header.ChainId) + if err != nil { + return fmt.Errorf("invalid endorsement") + } + + return nil +} + +func (vr *Verifier) Update(ctx context.Context, lbn *types.BlockNotification) error { + vr.mu.Lock() + defer vr.mu.Unlock() + + header := lbn.Header + block := lbn.Block + blockFittness := header.Fitness + + currentFittness, err := strconv.ParseInt(string(blockFittness[1].String()), 16, 64) + if err != nil { + return err + } + + vr.parentFittness = currentFittness + vr.parentHash = block.Hash + vr.height = header.Level + vr.next = header.Level + 1 + + if vr.cycle != block.Metadata.LevelInfo.Cycle { + vr.updateValidatorsAndCycle(ctx, block.Header.Level, block.Metadata.LevelInfo.Cycle) + } + + if vr.updatedBn == nil { + vr.updatedBn = lbn + return nil + } + vr.lastVerifiedBn = vr.updatedBn + vr.updatedBn = lbn + return nil +} + +func (vr *Verifier) ParentHash() tezos.BlockHash { + vr.mu.RLock() + defer vr.mu.RUnlock() + return vr.parentHash +} + +func (vr *Verifier) LastVerifiedBn() *types.BlockNotification { + vr.mu.RLock() + defer vr.mu.RUnlock() + return vr.lastVerifiedBn +} + +func (vr *Verifier) Height() int64 { + vr.mu.RLock() + defer vr.mu.RUnlock() + return vr.height +} + +func (vr *Verifier) IsValidator(proposer tezos.Address, height int64) bool { + vr.mu.RLock() + defer vr.mu.RUnlock() + return true +} + +func (vr *Verifier) VerifySignature(ctx context.Context, lbn *types.BlockNotification) (bool, error) { + header := lbn.Block.Header + + blockHeader := codec.BlockHeader{ + Level: int32(header.Level), + Proto: byte(header.Proto), + Predecessor: header.Predecessor, + Timestamp: header.Timestamp, + ValidationPass: byte(header.ValidationPass), + OperationsHash: header.OperationsHash, + Fitness: header.Fitness, + Context: header.Context, + PayloadHash: header.PayloadHash, + PayloadRound: header.PayloadRound, + ProofOfWorkNonce: header.ProofOfWorkNonce, + LbToggleVote: header.LbVote(), + SeedNonceHash: lbn.Block.Metadata.NonceHash, + ChainId: &lbn.Block.ChainId, + } + + digestedHash := blockHeader.Digest() + + err := vr.validatorsPublicKey[lbn.Block.Metadata.Baker].Verify(digestedHash[:], header.Signature) + + if err != nil { + return false, err + } + return true, nil +} + +func (vr *Verifier) updateValidatorsAndCycle(ctx context.Context, blockHeight int64, cycle int64) error { + validatorsList, err := vr.cl.Cl.ListEndorsingRights(ctx, rpc.BlockLevel(blockHeight)) + if err != nil { + return err + } + + bakersList, err := vr.cl.Cl.ListBakingRightsCycle(ctx, rpc.BlockLevel(blockHeight), cycle, 1) + if err != nil { + return err + } + + var validatorsPublicKey tezos.Key + // remove all validators + for a := range vr.validators { + delete(vr.validators, a) + } + vr.validators = make(map[tezos.Address]bool) + // add new validators + for _, validator := range validatorsList { + vr.validators[validator.Delegate] = true + validatorsPublicKey, err = vr.cl.GetConsensusKey(ctx, validator.Delegate) + if err != nil { + return err + } + vr.validatorsPublicKey[validator.Delegate] = validatorsPublicKey + } + + for _, validator := range bakersList { + if !vr.validators[validator.Delegate] { + vr.validators[validator.Delegate] = true + validatorsPublicKey, err = vr.cl.GetConsensusKey(ctx, validator.Delegate) + if err != nil { + return err + } + vr.validatorsPublicKey[validator.Delegate] = validatorsPublicKey + } + } + + vr.cycle = cycle + return nil +} + +func (vr *Verifier) verifyEndorsement(block *rpc.Block, chainID tezos.ChainIdHash) error { + endorsementPower := 0 + endorsers := make(map[tezos.Address]bool) + op := block.Operations + for i := 0; i < len(op); i++ { + for j := 0; j < len(op[i]); j++ { + for _, operation := range op[i][j].Contents { + signature := op[i][j].Signature + branch := op[i][j].Branch + switch operation.Kind() { + case tezos.OpTypeEndorsement: + tx := operation.(*rpc.Endorsement) + + if _, isDelegate := vr.validators[tx.Metadata.Delegate]; isDelegate { + endorsement := codec.TenderbakeEndorsement{ + Slot: int16(tx.Slot), + Level: int32(tx.Level), + Round: int32(tx.Round), + BlockPayloadHash: tx.PayloadHash, + } + digested := codec.NewOp().WithContentsFront(&endorsement).WithChainId(block.ChainId).WithBranch(branch).Digest() + + managerKey := vr.validatorsPublicKey[tx.Metadata.Delegate] + + err := managerKey.Verify(digested[:], signature) + + if err != nil { + return err + } + + if _, ok := endorsers[tx.Metadata.Delegate]; !ok { + endorsers[tx.Metadata.Delegate] = true + endorsementPower += tx.Metadata.EndorsementPower + } + } + } + } + } + } + if endorsementPower > int(threshold) { + return nil + } + return errors.New("endorsement verification failed") + +} + +type VerifierOptions struct { + BlockHeight int64 `json:"blockHeight"` + BlockHash tezos.BlockHash `json:"parentHash"` +} diff --git a/cmd/iconbridge/example.config.json b/cmd/iconbridge/example.config.json deleted file mode 100644 index 8c51d305e..000000000 --- a/cmd/iconbridge/example.config.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "base_dir": "bmr", - "log_level": "debug", - "console_level": "trace", - "log_writer": { - "filename": "bmr/bmr.log" - }, - "stat_collector": { - "verbose": false - }, - "log_forwarder": { - "vendor": "slack", - "address": "https://hooks.slack.com/services/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", - "level": "info" - }, - "relays": [ - { - "name": "h2i", - "src": { - "address": "btp://0x6357d2e0.hmny/0x233909bE3797BBd135A837AC945bCdE3cB078969", - "endpoint": ["https://rpc.s0.b.hmny.io"], - "options": { - "verifier": { - "blockHeight": 24629415, - "commitBitmap": "0xffffff07", - "commitSignature": "0xaa325f4ae32c183e184e4f64603cfbeb44cec12cb7654a180ed83b737d53ff86463f5c6ba20300b70b60266512ee570fba6bcfbc5085a07f39dac5a197e53d2eff450c9d8abdfa685a47a72a8bfb9d541fdf3cf02e8b3e89c4c3fa057d58dc8b" - }, - "syncConcurrency": 100 - }, - "offset": 24629946 - }, - "dst": { - "address": "btp://0x7.icon/cx9e5c0a749ee94c01febe04702184002a76a84f84", - "endpoint": [ - "https://berlin.net.solidwallet.io/api/v3/icon_dex" - ], - "options": { - "step_limit": 13610920010, - "tx_data_size_limit": 65536 - }, - "key_store": { - "address": "hxca02e14958183eef1a31d405e6628d25bdb35282", - "id": "f94b9e44-63a8-4b10-a691-a78cf4adb143", - "version": 3, - "coinType": "icx", - "crypto": { - "cipher": "aes-128-ctr", - "cipherparams": { - "iv": "081fcf6387ff3e72418561c22e203449" - }, - "ciphertext": "e959dbe55a64de209c69bb15fb110bb8ffbb6361400b530861eded26f686025d", - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "n": 65536, - "r": 8, - "p": 1, - "salt": "e55aed7374098c0a" - }, - "mac": "e05a40a5901fecddfc692653c1a6f1b3ea872b4fad2078f3b2aacb8fbbea7a8e" - } - }, - "key_password": "xyz" - } - }, - { - "name": "i2h", - "src": { - "address": "btp://0x7.icon/cx9e5c0a749ee94c01febe04702184002a76a84f84", - "endpoint": [ - "https://berlin.net.solidwallet.io/api/v3/icon_dex" - ], - "options": { - "verifier": { - "blockHeight": 50833960, - "validatorsHash": "0x120c4d12ae3770b868e650e950200364fe138b92e872e487f50da4337bcc83c7" - } - }, - "offset": 6057269 - }, - "dst": { - "address": "btp://0x6357d2e0.hmny/0x233909bE3797BBd135A837AC945bCdE3cB078969", - "endpoint": ["https://rpc.s0.b.hmny.io"], - "options": { - "gas_limit": 80000000, - "boost_gas_price": 1.5, - "tx_data_size_limit": 65536 - }, - "key_store": { - "address": "80f1b32f3d656528a9616076e42fcc4d47a5a9a3", - "crypto": { - "cipher": "aes-128-ctr", - "ciphertext": "8d892d72f030610af8df4625abb10fec8aaa62a3d882fde1d8875302f732f99d", - "cipherparams": { - "iv": "01c84057179e9dc95baaabc78e04918c" - }, - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "n": 262144, - "p": 1, - "r": 8, - "salt": "0c1b6b301a2d2d46f0a17b736b82fb4cdf90bb3a9be4a0671828c00d878a7d77" - }, - "mac": "f3ce22f3ba8fa0cd17d25a4be5752f8de53d3e52ec282f1ff050a3b3bcbdf683" - }, - "id": "982c4409-864d-408e-a1d0-ec2b47e319ba", - "version": 3, - "coinType": "evm" - }, - "key_password": "xyz" - } - } - ] -} diff --git a/cmd/iconbridge/main.go b/cmd/iconbridge/main.go index 53fac58a8..599000b29 100644 --- a/cmd/iconbridge/main.go +++ b/cmd/iconbridge/main.go @@ -23,6 +23,7 @@ import ( _ "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/icon" _ "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/near" _ "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/substrate-eth" + _ "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/tezos" ) var ( diff --git a/cmd/iconbridge/relay/multi_relay.go b/cmd/iconbridge/relay/multi_relay.go index 4a100db44..ed53399cb 100644 --- a/cmd/iconbridge/relay/multi_relay.go +++ b/cmd/iconbridge/relay/multi_relay.go @@ -56,7 +56,7 @@ func NewMultiRelay(cfg *Config, l log.Logger) (Relay, error) { } l := l.WithFields(log.Fields{ log.FieldKeyModule: rc.Name, - log.FieldKeyWallet: w.Address(), + // log.FieldKeyWallet: w.Address(), log.FieldKeyService: srvName, }) diff --git a/cmd/iconbridge/relay/relay.go b/cmd/iconbridge/relay/relay.go index 5d111fdf4..423ce6582 100644 --- a/cmd/iconbridge/relay/relay.go +++ b/cmd/iconbridge/relay/relay.go @@ -14,7 +14,7 @@ import ( const ( relayTickerInterval = 5 * time.Second relayBalanceCheckInterval = 60 * time.Second - relayTriggerReceiptsCount = 20 + relayTriggerReceiptsCount = 5 relayTxSendWaitInterval = time.Second / 2 relayTxReceiptWaitInterval = time.Second relayInsufficientBalanceWaitInterval = 30 * time.Second @@ -157,7 +157,6 @@ func (r *relay) Start(ctx context.Context) error { } } msg.Receipts = receipts - if len(msg.Receipts) > 0 { r.log.WithFields(log.Fields{ "seq": []uint64{seqBegin, seqEnd}}).Debug("srcMsg added") @@ -168,7 +167,6 @@ func (r *relay) Start(ctx context.Context) error { } case <-relayCh: - link, err = r.dst.Status(ctx) if err != nil { r.log.WithFields(log.Fields{"error": err}).Debug("dst.Status: failed") @@ -188,7 +186,7 @@ func (r *relay) Start(ctx context.Context) error { r.log.WithFields(log.Fields{"rxSeq": missing}).Error("missing event sequence") return fmt.Errorf("missing event sequence") } - + tx, newMsg, err := r.dst.Segment(ctx, srcMsg) if err != nil { return err diff --git a/common/address.go b/common/address.go index 1708c2319..bc1f455f8 100644 --- a/common/address.go +++ b/common/address.go @@ -6,6 +6,7 @@ import ( "encoding/json" "reflect" + // "github.com/btcsuite/btcutil/base58" "github.com/icon-project/icon-bridge/common/codec" "github.com/icon-project/icon-bridge/common/crypto" "github.com/icon-project/icon-bridge/common/errors" @@ -46,22 +47,25 @@ func (a *Address) UnmarshalJSON(b []byte) error { func (a *Address) SetString(s string) error { var isContract = false if len(s) >= 2 { + var err error + var bytes []byte + prefix := s[0:2] switch { - case s[0:2] == "cx": - isContract = true - s = s[2:] - case s[0:2] == "hx": - s = s[2:] - case s[0:2] == "0x": + case prefix == "tz" || prefix == "KT": + isContract = prefix == "KT" + // bytes, _, err = base58.CheckDecode(s[2:]) + return nil + case prefix == "cx" || prefix == "hx" || prefix == "0x": + isContract = prefix == "cx" s = s[2:] + if len(s)%2 == 1 { + s = "0" + s + } + bytes, err = hex.DecodeString(s) + } + if err != nil { + return err } - } - if len(s)%2 == 1 { - s = "0" + s - } - if bytes, err := hex.DecodeString(s); err != nil { - return err - } else { if err := a.SetTypeAndID(isContract, bytes); err != nil { return err } diff --git a/common/wallet/keystore.go b/common/wallet/keystore.go index b913aaca0..4041af3e2 100644 --- a/common/wallet/keystore.go +++ b/common/wallet/keystore.go @@ -10,6 +10,7 @@ import ( "fmt" "io" + "blockwatch.cc/tzgo/tezos" "github.com/gofrs/uuid" "golang.org/x/crypto/scrypt" "golang.org/x/crypto/sha3" @@ -26,6 +27,7 @@ const ( cipherAES128CTR = "aes-128-ctr" kdfScrypt = "scrypt" coinTypeNear = "near" + coinTypeXTZ = "xtz" ) type AES128CTRParams struct { @@ -177,6 +179,10 @@ func DecryptKeyStore(data, pw []byte) (Wallet, error) { return nil, err } return NewNearwalletFromPrivateKey(key) + case coinTypeXTZ: + key := tezos.MustParsePrivateKey(ksdata.Crypto.Cipher) + + return NewTezosWalletFromPrivateKey(key) default: return nil, errors.Errorf("InvalidCoinType(coin=%s)", ksdata.CoinType) } diff --git a/common/wallet/wallet_tezos.go b/common/wallet/wallet_tezos.go new file mode 100644 index 000000000..b750dfd86 --- /dev/null +++ b/common/wallet/wallet_tezos.go @@ -0,0 +1,41 @@ +package wallet + +import ( + "errors" + + "blockwatch.cc/tzgo/signer" + "blockwatch.cc/tzgo/tezos" +) + +type TezosWallet struct { + Skey tezos.PrivateKey + Pkey tezos.Key +} + +func (w *TezosWallet) Address() string { + return w.Pkey.Address().String() +} + +func (w *TezosWallet) Sign(data []byte) ([]byte, error) { + return nil, errors.New("not allowed, use Signer instead") +} + +func (w *TezosWallet) Signer() *signer.MemorySigner { + return signer.NewFromKey(w.Skey) +} + +func (w *TezosWallet) PublicKey() []byte { + return w.Pkey.Bytes() +} + +func (w *TezosWallet) ECDH(pubKey []byte) ([]byte, error) { + //TODO: Not implemented yet + return nil, nil +} + +func NewTezosWalletFromPrivateKey(sk tezos.PrivateKey) (*TezosWallet, error) { + return &TezosWallet{ + Skey: sk, + Pkey: sk.Public(), + }, nil +} diff --git a/devnet/docker/icon-tezos/bmr.Dockerfile b/devnet/docker/icon-tezos/bmr.Dockerfile new file mode 100644 index 000000000..992b8b1c0 --- /dev/null +++ b/devnet/docker/icon-tezos/bmr.Dockerfile @@ -0,0 +1,42 @@ +# dev build +FROM ubuntu:18.04 + +ARG TARGETARCH +ARG GOLANG_VERSION="1.20" + +SHELL ["/bin/bash", "-c"] + +ENV GOPATH=/root/go +ENV GO111MODULE=on +ENV GIMME_GO_VERSION=${GOLANG_VERSION} +ENV PATH="/root/bin:${PATH}" + +RUN apt update && apt upgrade -y && \ + apt install libgmp-dev libssl-dev curl git \ + psmisc dnsutils jq make gcc g++ bash tig tree sudo vim \ + silversearcher-ag unzip emacs-nox nano bash-completion -y + +RUN mkdir ~/bin && \ + curl -sL -o ~/bin/gimme \ + https://raw.githubusercontent.com/travis-ci/gimme/master/gimme && \ + chmod +x ~/bin/gimme + +RUN eval "$(~/bin/gimme ${GIMME_GO_VERSION})" +RUN touch /root/.bash_profile && \ + gimme ${GIMME_GO_VERSION} >> /root/.bash_profile && \ + echo "GIMME_GO_VERSION='${GIMME_GO_VERSION}'" >> /root/.bash_profile && \ + echo "GO111MODULE='on'" >> /root/.bash_profile && \ + echo ". ~/.bash_profile" >> /root/.profile && \ + echo ". ~/.bash_profile" >> /root/.bashrc +ENV PATH="/root/.gimme/versions/go${GIMME_GO_VERSION}.linux.${TARGETARCH:-amd64}/bin:${GOPATH}/bin:${PATH}" +RUN . ~/.bash_profile + +COPY . bmr +RUN cd bmr/cmd/iconbridge + +# prod build +FROM ubuntu:18.04 +SHELL ["/bin/bash", "-c"] +RUN apt update -y && apt install -y make ca-certificates libssl-dev +COPY --from=0 /bmr/cmd/iconbridge/iconbridge /bin/iconbridge +RUN rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/devnet/docker/icon-tezos/docker-compose-bmr.yml b/devnet/docker/icon-tezos/docker-compose-bmr.yml new file mode 100644 index 000000000..8a4816925 --- /dev/null +++ b/devnet/docker/icon-tezos/docker-compose-bmr.yml @@ -0,0 +1,15 @@ +version: '3.3' +services: + bmr: + image: i2tbmr2:latest + container_name: bmr + network_mode: host + restart: unless-stopped + # ports: + # - 6060:6060 # golang pprof + entrypoint: ["/bin/iconbridge", "-config", "/config.json"] + volumes: + - /home/sheldor/GoProjects/icon-bridge/devnet/docker/icon-tezos/_ixh/relay.config.json:/config.json + #environment: + #AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + #AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} diff --git a/devnet/docker/icon-tezos/scripts/config/icon-tezos.config.testnet.sh b/devnet/docker/icon-tezos/scripts/config/icon-tezos.config.testnet.sh new file mode 100644 index 000000000..112ca8d9b --- /dev/null +++ b/devnet/docker/icon-tezos/scripts/config/icon-tezos.config.testnet.sh @@ -0,0 +1,76 @@ +#!/bin/bash +#BUILD_DIR=$(echo "$(cd "$(dirname "../../../../../")"; pwd)"/build) +BASE_DIR=$(echo "$(cd "$(dirname "../../")"; pwd)") +BUILD_DIR=$BASE_DIR/build +export ICONBRIDGE_CONFIG_DIR=$BASE_DIR/_ixh +export ICONBRIDGE_CONTRACTS_DIR=$BUILD_DIR/contracts +export ICONBRIDGE_SCRIPTS_DIR=$BASE_DIR/scripts +export ICONBRIDGE_BIN_DIR=$BASE_DIR + +export CONFIG_DIR=${CONFIG_DIR:-${ICONBRIDGE_CONFIG_DIR}} +export CONTRACTS_DIR=${CONTRACTS_DIR:-${ICONBRIDGE_CONTRACTS_DIR}} +export SCRIPTS_DIR=${SCRIPTS_DIR:-${ICONBRIDGE_SCRIPTS_DIR}} + +################################################################################### +# testnet: begin +export TAG="ICON BSC TESTNET" +export BSC_BMC_NET="0x61.bsc" +export ICON_BMC_NET="0x2.icon" +export SNOW_BMC_NET="0x229.snow" +export TZ_BMC_NET="0x63.tezos" +export GOLOOP_RPC_NID="0x2" +export BSC_NID="97" +export TEZOS_NID="NetXnHfVqm9iesp" + +export ICON_ENDPOINT="https://lisbon.net.solidwallet.io/api/v3/icon_dex" +export ICON_NATIVE_COIN_SYM=("ICX") +export ICON_NATIVE_COIN_NAME=("btp-$ICON_BMC_NET-ICX") +export ICON_NATIVE_TOKEN_SYM=("sICX" "bnUSD") +export ICON_NATIVE_TOKEN_NAME=("btp-$ICON_BMC_NET-sICX" "btp-$ICON_BMC_NET-bnUSD") +export ICON_WRAPPED_COIN_SYM=("BNB" "BUSD" "USDT" "USDC" "BTCB" "ETH" "ICZ") +export ICON_WRAPPED_COIN_NAME=("btp-$BSC_BMC_NET-BNB" "btp-$BSC_BMC_NET-BUSD" "btp-$BSC_BMC_NET-USDT" "btp-$BSC_BMC_NET-USDC" "btp-$BSC_BMC_NET-BTCB" "btp-$BSC_BMC_NET-ETH" "btp-$SNOW_BMC_NET-ICZ") +export FEE_GATHERING_INTERVAL=21600 + + +export ICON_NATIVE_COIN_FIXED_FEE=(4300000000000000000) +export ICON_NATIVE_COIN_FEE_NUMERATOR=(100) +export ICON_NATIVE_COIN_DECIMALS=(18) +export ICON_NATIVE_TOKEN_FIXED_FEE=(3900000000000000000 1500000000000000000) +export ICON_NATIVE_TOKEN_FEE_NUMERATOR=(100 100) +export ICON_NATIVE_TOKEN_DECIMALS=(18 18) +export ICON_WRAPPED_COIN_FIXED_FEE=(5000000000000000 1500000000000000000 1500000000000000000 1500000000000000000 62500000000000 750000000000000 4300000000000000000) +export ICON_WRAPPED_COIN_FEE_NUMERATOR=(100 100 100 100 100 100 100) +export ICON_WRAPPED_COIN_DECIMALS=(18 18 18 18 18 18 18) + +export TZ_ENDPOINT="https://ghostnet.tezos.marigold.dev" +export TZ_NATIVE_COIN_SYM=("XTZ") +export TZ_NATIVE_COIN_NAME=("btp-$TZ_BMC_NET-TZ") +export TZ_NATIVE_TOKEN_SYM=("BUSD" "USDT" "USDC" "BTCB" "ETH") +export TZ_NATIVE_TOKEN_NAME=("btp-$TZ_BMC_NET-BUSD" "btp-$TZ_BMC_NET-USDT" "btp-$TZ_BMC_NET-USDC" "btp-$TZ_BMC_NET-BTCB" "btp-$TZ_BMC_NET-ETH") +export TZ_WRAPPED_COIN_SYM=("ICX" "sICX" "bnUSD" "ICZ") +export TZ_WRAPPED_COIN_NAME=("btp-$ICON_BMC_NET-ICX" "btp-$ICON_BMC_NET-sICX" "btp-$ICON_BMC_NET-bnUSD" "btp-$SNOW_BMC_NET-ICZ") + +export TZ_NATIVE_COIN_FIXED_FEE=(1) +export TZ_NATIVE_COIN_FEE_NUMERATOR=(100) +export TZ_NATIVE_COIN_DECIMALS=(6) +export TZ_NATIVE_TOKEN_FIXED_FEE=(1500000000000000000 1500000000000000000 1500000000000000000 62500000000000 750000000000000) +export TZ_NATIVE_TOKEN_FEE_NUMERATOR=(100 100 100 100 100) +export TZ_NATIVE_TOKEN_DECIMALS=(18 18 18 18 18) +export TZ_WRAPPED_COIN_FIXED_FEE=(4300000000000000000 3900000000000000000 1500000000000000000 4300000000000000000) +export TZ_WRAPPED_COIN_FEE_NUMERATOR=(100 100 100 100) +export TZ_WRAPPED_COIN_DECIMALS=(18 18 18 18) + +# testnet: end +################################################################################### + +GOLOOPCHAIN=${GOLOOPCHAIN:-"goloop"} +export GOLOOP_RPC_STEP_LIMIT=5000000000 +export ICON_KEY_STORE=$ICONBRIDGE_CONFIG_DIR/keystore/icon.god.wallet.json +export ICON_SECRET=$ICONBRIDGE_CONFIG_DIR/keystore/icon.god.wallet.secret +export GOLOOP_RPC_URI=$ICON_ENDPOINT +export GOLOOP_RPC_KEY_STORE=$ICON_KEY_STORE +export GOLOOP_RPC_KEY_SECRET=$ICON_SECRET + +export TZ_RPC_URI=$BSC_ENDPOINT +export TZ_KEY_STORE=$ICONBRIDGE_CONFIG_DIR/keystore/tz.god.wallet.json +# export BSC_SECRET=$ICONBRIDGE_CONFIG_DIR/keystore/bsc.god.wallet.secret diff --git a/devnet/docker/icon-tezos/scripts/mainnet.i2t.bmr.sh b/devnet/docker/icon-tezos/scripts/mainnet.i2t.bmr.sh new file mode 100755 index 000000000..b568c42b2 --- /dev/null +++ b/devnet/docker/icon-tezos/scripts/mainnet.i2t.bmr.sh @@ -0,0 +1,815 @@ +#!/bin/bash +## smarpy service methods - start ### + +# source utils.sh +# source ~/GoProjects/icon-bridge/devnet/docker/icon-bsc/scripts/rpc.sh +# source keystore.sh + +export ICON_NET_ID=0x1 +export ICON_BMC_NID=$ICON_NET_ID.icon +export ICON_NET_URI=https://ctz.solidwallet.io/api/v3/ +export TEZOS_BMC_NID=NetXdQprcVkpaWU.tezos +export TZ_NET_URI=https://mainnet.tezos.marigold.dev/ + +export BASE_DIR=$(echo $(pwd))/../../../.. +export CONFIG_DIR=$BASE_DIR/devnet/docker/icon-tezos +export TEZOS_SETTER=$BASE_DIR/tezos-addresses +export JAVASCORE_DIR=$BASE_DIR/javascore +export SMARTPY_DIR=$BASE_DIR/smartpy +export CONTRACTS_DIR=$BASE_DIR +export TZ_NATIVE_COIN_NAME=btp-$TEZOS_BMC_NID.XTZ +export TZ_COIN_SYMBOL=XTZ +export TZ_FIXED_FEE=0 +export TZ_NUMERATOR=0 +export TZ_DECIMALS=6 +export ICON_NATIVE_COIN_NAME=btp-$ICON_NET_ID.icon-ICX +export ICON_SYMBOL=ICX +export ICON_FIXED_FEE=0 +export ICON_NUMERATOR=0 +export ICON_DECIMALS=18 +export FEE_GATHERING_INTERVAL=43200 +export ICON_ZERO_ADDRESS=cx0000000000000000000000000000000000000000 + +tz_lastBlock() { + octez-client rpc get /chains/main/blocks/head/header +} + +extract_chainHeight() { + cd $CONFIG_DIR/_ixh + local tz_block_height=$(tz_lastBlock | jq -r .level) + echo $tz_block_height > tz.chain.height +} + +ensure_config_dir() { + echo ensuring config dir + cd $CONFIG_DIR + if [ ! -d _ixh ]; then + echo _ixh not found so creating one + mkdir _ixh + fi + if [ ! -d $CONFIG_DIR/_ixh/tx ]; then + echo tx not found so creating one + cd _ixh + mkdir tx + fi +} + +ensure_tezos_keystore(){ + echo "ensuring key store" + cd $CONFIG_DIR/_ixh/keystore + if [ ! -f tz.bmr.wallet ]; then + echo "creating tezos bmr wallet" + octez-client forget address bmr --force + octez-client gen keys bmr + local keystore=$(echo $(octez-client show address bmr -S)) + local keystoreClone=$keystore + keystore_secret=${keystore#*Secret Key: unencrypted:} + keystore_hash=${keystoreClone#*Hash: } + keystore_hash=${keystore_hash%% *} + echo $keystore_hash > tz.bmr.wallet + echo $keystore_secret > tz.bmr.wallet.secret + fi + + # cd $(echo $SMARTPY_DIR/bmc) + # if [ -f .env ]; then + # echo ".env found" + # octez-client forget address bmcOwner --force + # octez-client gen keys bmcOwner + # local keystore=$(echo $(octez-client show address bmcOwner -S)) + # local keystoreClone=$keystore + # keystore_secret=${keystore#*Secret Key: unencrypted:} + # keystore_hash=${keystoreClone#*Hash: } + # keystore_hash=${keystore_hash%% *} + # echo $keystore_hash > tz.bmc.wallet + # echo $keystore_secret > .env + # fi + + # cd $SMARTPY_DIR/bts + # if [ -f .env ]; then + # echo ".env found" + # octez-client forget address btsOwner --force + # octez-client gen keys btsOwner + # local keystore=$(echo $(octez-client show address btsOwner -S)) + # local keystoreClone=$keystore + # keystore_secret=${keystore#*Secret Key: unencrypted:} + # keystore_hash=${keystoreClone#*Hash: } + # keystore_hash=${keystore_hash%% *} + # echo $keystore_hash > tz.bts.wallet + # echo $keystore_secret > .env + # fi + +} + +ensure_key_secret() { + if [ $# -lt 1 ] ; then + echo "Usage: ensure_key_secret SECRET_PATH" + return 1 + fi + local KEY_SECRET=$1 + if [ ! -f "${KEY_SECRET}" ]; then + mkdir -p $(dirname ${KEY_SECRET}) + echo -n $(openssl rand -hex 20) > ${KEY_SECRET} + fi + echo ${KEY_SECRET} +} + +ensure_key_store() { + if [ $# -lt 2 ] ; then + echo "Usage: ensure_key_store KEYSTORE_PATH SECRET_PATH" + return 1 + fi + local KEY_STORE=$1 + local KEY_SECRET=$(ensure_key_secret $2) + if [ ! -f "${KEY_STORE}" ]; then + echo should not reach here + goloop ks gen --out ${KEY_STORE}tmp -p $(cat ${KEY_SECRET}) > /dev/null 2>&1 + cat ${KEY_STORE}tmp | jq -r . > ${KEY_STORE} + rm ${KEY_STORE}tmp + + fi + echo ${KEY_STORE} +} + +fund_it_flag() { + cd $CONFIG_DIR + if [ ! -f fundit.flag ]; then + echo Fund the recently created wallet and run the script once again + echo icon bmc wallet: $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json | jq -r .address) + echo icon bts wallet: $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json | jq -r .address) + echo icon bmr wallet: $(cat $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.json | jq -r .address) + echo icon fa wallet : $(cat $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.json | jq -r .address) + echo tz bmr wallet : $(cat $CONFIG_DIR/_ixh/keystore/tz.bmr.wallet) + + # echo tz bmc wallet : $(cat $SMARTPY_DIR/bmc/tz.bmc.wallet) + # echo tz bts wallet : $(cat $SMARTPY_DIR/bts/tz.bts.wallet) + + echo fund it flag > fundit.flag + exit 0 + fi +} + +deploy_smartpy_bmc_management(){ + cd $(echo $SMARTPY_DIR/bmc) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bmcmanagementbtp ]; then + echo "deploying bmc_management" + extract_chainHeight + cd $SMARTPY_DIR/bmc + npm run compile bmc_management + local deploy=$(npm run deploy bmc_management @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bmc_management + cd $CONFIG_DIR/_ixh + echo "btp://$(echo $TEZOS_BMC_NID)/$(cat tz.addr.bmc_management)" > $CONFIG_DIR/_ixh/tz.addr.bmcmanagementbtp + fi +} + +deploy_smartpy_bmc_periphery(){ + cd $(echo $SMARTPY_DIR/bmc) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bmcperipherybtp ]; then + echo "deploying bmc_periphery" + npm run compile bmc_periphery + local deploy=$(npm run deploy bmc_periphery @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bmc_periphery + cd $CONFIG_DIR/_ixh + echo "btp://$(echo $TEZOS_BMC_NID)/$(cat tz.addr.bmc_periphery)" > $CONFIG_DIR/_ixh/tz.addr.bmcperipherybtp + fi +} + +deploy_smartpy_bts_periphery(){ + cd $(echo $SMARTPY_DIR/bts) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bts_periphery ]; then + echo "deploying bts_periphery" + npm run compile bts_periphery + local deploy=$(npm run deploy bts_periphery @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bts_periphery + fi +} + +deploy_smartpy_bts_core(){ + cd $(echo $SMARTPY_DIR/bts) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bts_core ]; then + echo "deploying bts_core" + npm run compile bts_core + local deploy=$(npm run deploy bts_core @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bts_core + fi +} + +deploy_smartpy_bts_owner_manager(){ + cd $(echo $SMARTPY_DIR/bts) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bts_owner_manager ]; then + echo "deploying bts_owner_manager" + npm run compile bts_owner_manager + local deploy=$(npm run deploy bts_owner_manager @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bts_owner_manager + fi +} + +ensure_txresult() { + OLD_SET_OPTS=$(set +o) + set +e + local TX=$1 + + if [ -f "${TX}" ]; then + TX=$(cat ${TX}) + fi + + sleep 2 + RESULT=$(goloop rpc txresult ${TX} --uri $ICON_NET_URI) + RET=$? + echo $RESULT + while [ "$RET" != "0" ] && [ "$(echo $RESULT | grep -E 'Executing|Pending')" == "$RESULT" ]; do + sleep 1 + RESULT=$(goloop rpc txresult ${TX} --rui $ICON_NET_URI) + RET=$? + echo $RESULT + done + eval "${OLD_SET_OPTS}" + set -e + if [ "$RET" != "0" ]; then + echo $RESULT + return $RET + else + STATUS=$(echo $RESULT | jq -r .status) + if [ "$STATUS" == "0x1" ]; then + return 0 + else + echo $RESULT + return 1 + fi + fi +} + +extract_scoreAddress() { + local TX=$1 + local ADDR=$2 + + RESULT=$(ensure_txresult $TX) + RET=$? + + if [ "$RET" != "0" ]; then + echo $RESULT + return $RET + else + SCORE=$(echo $RESULT | jq -r .scoreAddress) + echo $SCORE | tee ${ADDR} + fi +} + + +configure_smartpy_bmc_management_set_bmc_periphery() { + echo "Adding BMC periphery in bmc management" + cd $(echo $CONFIG_DIR/_ixh) + + local bmc_periphery=$(echo $(cat tz.addr.bmc_periphery)) + echo $bmc_periphery + + local bmc_management=$(echo $(cat tz.addr.bmc_management)) + echo $bmc_management + + local ocBR=\'\" + local coBR=\"\' + local arg=$(echo $(echo $ocBR$(echo $bmc_periphery$(echo $coBR)))) + + echo $arg + + # octez-client transfer 0 from bmcOwner to KT1BE6ohnjunYd1C96yPaThwNvFZu4TN8iBy --entrypoint set_bmc_periphery --arg '"KT1JX3Z3AQnf6oDae87Z9mw1g4jhB38tAGQY"' --burn-cap 1 + echo octez-client transfer 0 from bmcOwner to $(echo $bmc_management) --entrypoint set_bmc_periphery --arg $(echo $arg) --burn-cap 1 +} + +configure_dotenv() { + echo "Configuring .env file for running the setter script" + cd $(echo $CONFIG_DIR/_ixh) + local bmc_periphery=$(echo $(cat tz.addr.bmc_periphery)) + local bmc_management=$(echo $(cat tz.addr.bmc_management)) + local bmc_height=$(echo $(cat tz.chain.height)) + local icon_bmc_height=$(echo $(cat icon.chain.height)) + local icon_bmc=$(echo $(cat icon.addr.bmc)) + echo $bmc_periphery + + local bts_core=$(echo $(cat tz.addr.bts_core)) + local bts_owner_manager=$(echo $(cat tz.addr.bts_owner_manager)) + local bts_periphery=$(echo $(cat tz.addr.bts_periphery)) + + cd $SMARTPY_DIR/bmc + local env=$(cat .env) + env=${env#*=} + local secret_deployer=$(echo "secret_deployer=$(echo $env)") + + cd $(echo $TEZOS_SETTER) + go mod tidy + if [ -f .env ]; then + echo ".env exists so removing" + rm .env + fi + touch .env + local output=.env + + + local TZ_NETWORK=$(echo "TZ_NETWORK=$(echo $TEZOS_BMC_NID)") + local ICON_NETWORK=$(echo "ICON_NETWORK=$(echo $ICON_BMC_NID)") + local TEZOS_NATIVE_COIN_NAME=$(echo "TZ_NATIVE_COIN_NAME=btp-$(echo $TEZOS_BMC_NID)-XTZ") + local TEZOS_SYMBOL=$(echo "TZ_SYMBOL=$(echo $TZ_COIN_SYMBOL)") + local TEZ_FIXED_FEE=$(echo "TZ_FIXED_FEE=$(echo $TZ_FIXED_FEE)") + + local TEZ_NUMERATOR=$(echo "TZ_NUMERATOR=$(echo $TZ_NUMERATOR)") + local TEZ_DECIMALS=$(echo "TZ_DECIMALS=$(echo $TZ_DECIMALS)") + local IC_NATIVE_COIN_NAME=$(echo "ICON_NATIVE_COIN_NAME=$(echo $ICON_NATIVE_COIN_NAME)") + + local IC_SYMBOL=$(echo "ICON_SYMBOL=$(echo $ICON_SYMBOL)") + + local IC_FIXED_FEE=$(echo "ICON_FIXED_FEE=$(echo $ICON_FIXED_FEE)") + + local IC_NUMERATOR=$(echo "ICON_NUMERATOR=$(echo $ICON_NUMERATOR)") + local IC_DECIMALS=$(echo "ICON_DECIMALS=$(echo $ICON_DECIMALS)") + + local BMC_PERIPHERY=$(echo "BMC_PERIPHERY=$(echo $bmc_periphery)") + local BMC_MANAGEMENT=$(echo "BMC_MANAGEMENT=$(echo $bmc_management)") + local BMC_HEIGHT=$(echo "bmc_periphery_height=$(echo $bmc_height)") + + local BTS_PERIPHERY=$(echo "BTS_PERIPHERY=$(echo $bts_periphery)") + local BTS_CORE=$(echo "BTS_CORE=$(echo $bts_core)") + local BTS_OWNER_MANAGER=$(echo "BTS_OWNER_MANAGER=$(echo $bts_owner_manager)") + local RELAY_ADDRESS=$(echo "RELAYER_ADDRESS=$(echo $(cat $CONFIG_DIR/_ixh/keystore/tz.bmr.wallet))") + local ICON_BMC=$(echo "ICON_BMC=$(echo $icon_bmc)") + local ICON_RX_HEIGHT=$(echo "ICON_RX_HEIGHT=$(echo $icon_bmc_height)") + local TEZOS_ENDPOINT=$(echo "TEZOS_ENDPOINT=$(echo $TZ_NET_URI)") + + + echo $secret_deployer>>$output + + echo $TZ_NETWORK>>$output + echo $ICON_NETWORK>>$output + echo $TEZOS_NATIVE_COIN_NAME>>$output + echo $TEZOS_SYMBOL>>$output + echo $TEZ_FIXED_FEE>>$output + echo $TEZ_NUMERATOR>>$output + echo $TEZ_DECIMALS>>$output + echo $IC_NATIVE_COIN_NAME>>$output + echo $IC_SYMBOL>>$output + echo $IC_FIXED_FEE>>$output + echo $IC_NUMERATOR>>$output + echo $IC_DECIMALS>>$output + echo $BMC_PERIPHERY>>$output + echo $BMC_MANAGEMENT>>$output + echo $BMC_HEIGHT>>$output + echo $BTS_PERIPHERY>>$output + echo $BTS_CORE>>$output + echo $BTS_OWNER_MANAGER>>$output + echo $RELAY_ADDRESS>>$output + echo $ICON_BMC>>$output + echo $ICON_RX_HEIGHT>>$output + echo $TEZOS_ENDPOINT>>$output +} + +run_tezos_setters(){ + cd $(echo $TEZOS_SETTER) + go run main.go +} + + +# build java scores +build_javascores(){ + echo in java score + cd $JAVASCORE_DIR + ./gradlew bmc:optimizedJar + ./gradlew bts:optimizedJar + ./gradlew irc2Tradeable:optimizedJar + + # irc2-token + if [ ! -f irc2.jar ]; then + git clone https://github.com/icon-project/java-score-examples.git + cd java-score-examples + ./gradlew irc2-token:clean + ./gradlew irc2-token:optimizedJar + cp irc2-token/build/libs/irc2-token-0.9.1-optimized.jar $JAVASCORE_DIR/irc2.jar + rm -rf $JAVASCORE_DIR/java-score-examples + fi + + cd $JAVASCORE_DIR + cp bmc/build/libs/bmc-optimized.jar $JAVASCORE_DIR/bmc.jar + cp bts/build/libs/bts-optimized.jar $JAVASCORE_DIR/bts.jar + cp irc2Tradeable/build/libs/irc2Tradeable-0.1.0-optimized.jar $JAVASCORE_DIR/irc2Tradeable.jar +} + + + +# deploy java scores + +goloop_lastblock() { + goloop rpc lastblock --uri $ICON_NET_URI +} + +extract_chain_height_and_validator() { + cd $CONFIG_DIR/_ixh + local icon_block_height=$(goloop_lastblock | jq -r .height) + echo $icon_block_height > icon.chain.height + + local validator=$(HEIGHT=0x1 URI=$ICON_NET_URI $BASE_DIR/cmd/iconvalidators/./iconvalidators | jq -r .hash) + echo $validator > icon.chain.validators +} + +deploy_javascore_bmc() { + cd $CONFIG_DIR/_ixh + if [ ! -f icon.addr.bmcbtp ]; then + echo "deploying javascore BMC" + extract_chain_height_and_validator + goloop rpc sendtx deploy $CONTRACTS_DIR/javascore/bmc.jar \ + --content_type application/java \ + --param _net=$ICON_BMC_NID \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 4000000000 \ + --uri $ICON_NET_URI | jq -r . >tx/tx.icon.bmc + sleep 5 + echo $(pwd) + extract_scoreAddress tx/tx.icon.bmc icon.addr.bmc + echo "btp://$(echo $ICON_BMC_NID)/$(cat icon.addr.bmc)" >icon.addr.bmcbtp + fi +} + +deploy_javascore_bts() { + echo "deploying javascore bts" + cd $CONFIG_DIR/_ixh + if [ ! -f icon.addr.bts ]; then + #local bts_fee_numerator=100 + #local bts_fixed_fee=5000 + goloop rpc sendtx deploy $CONTRACTS_DIR/javascore/bts.jar \ + --content_type application/java \ + --param _name=$ICON_NATIVE_COIN_NAME \ + --param _bmc=$(cat icon.addr.bmc) \ + --param _decimals=$(decimal2Hex $3) \ + --param _feeNumerator=$(decimal2Hex $2) \ + --param _fixedFee=$(decimal2Hex $1) \ + --param _serializedIrc2=$(xxd -p $CONTRACTS_DIR/javascore/irc2Tradeable.jar | tr -d '\n') \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret)) \ + --step_limit 4000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . > tx/tx.icon.bts + sleep 5 + extract_scoreAddress tx/tx.icon.bts icon.addr.bts + fi +} + +deploy_javascore_token() { + echo "deploying javascore IRC2Token " $2 + cd $CONFIG_DIR/_ixh + if [ ! -f icon.addr.$2 ]; then + goloop rpc sendtx deploy $CONTRACTS_DIR/javascore/irc2.jar \ + --content_type application/java \ + --param _name="$1" \ + --param _symbol=$2 \ + --param _initialSupply="0x5f5e100" \ + --param _decimals="0x12" \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 4000000000 \ + --uri $ICON_NET_URI | jq -r . >tx/tx.icon.$2 + sleep 5 + extract_scoreAddress tx/tx.icon.$2 icon.addr.$2 + fi +} + + +configure_javascore_add_bmc_owner() { + echo "bmc Add Owner" + echo $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json + local icon_bmc_owner=$(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json | jq -r .address) + cd $CONFIG_DIR/_ixh + local is_owner=$(goloop rpc call \ + --to $(cat icon.addr.bmc) \ + --method isOwner \ + --param _addr=$icon_bmc_owner \ + --uri $ICON_NET_URI | jq -r .) + if [ "$is_owner" == "0x0" ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method addOwner \ + --param _addr=$icon_bmc_owner \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . > tx/addbmcowner.icon + sleep 3 + ensure_txresult tx/addbmcowner.icon + fi +} + +configure_javascore_bmc_setFeeAggregator() { + echo "bmc setFeeAggregator" + cd $CONFIG_DIR/_ixh + local FA=$(cat $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.json | jq -r .address) + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method setFeeAggregator \ + --param _addr=${FA} \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . >tx/setFeeAggregator.icon + sleep 3 + ensure_txresult tx/setFeeAggregator.icon + + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method setFeeGatheringTerm \ + --param _value=$FEE_GATHERING_INTERVAL \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . >tx/setFeeGatheringTerm.icon + sleep 3 + ensure_txresult tx/setFeeGatheringTerm.icon +} + +configure_javascore_add_bts() { + echo "bmc add bts" + cd $CONFIG_DIR/_ixh + local hasBTS=$(goloop rpc call \ + --to $(cat icon.addr.bmc) \ + --method getServices \ + --uri $ICON_NET_URI | jq -r .bts) + if [ "$hasBTS" == "null" ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method addService \ + --value 0 \ + --param _addr=$(cat icon.addr.bts) \ + --param _svc="bts" \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . >tx/addService.icon + sleep 3 + ensure_txresult tx/addService.icon + fi + sleep 5 +} + +configure_javascore_add_bts_owner() { + echo "Add bts Owner" + local icon_bts_owner=$(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json | jq -r .address) + cd $CONFIG_DIR/_ixh + local is_owner=$(goloop rpc call \ + --to $(cat icon.addr.bts) \ + --method isOwner \ + --param _addr="$icon_bts_owner" \ + --uri $ICON_NET_URI | jq -r .) + if [ "$is_owner" == "0x0" ]; then + goloop rpc sendtx call --to $(cat icon.addr.bts) \ + --method addOwner \ + --param _addr=$icon_bts_owner \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . >tx/addBtsOwner.icon + sleep 3 + ensure_txresult tx/addBtsOwner.icon + fi +} + +configure_javascore_bts_setICXFee() { + echo "bts set fee" $ICON_SYMBOL + #local bts_fee_numerator=100 + #local bts_fixed_fee=5000 + cd $CONFIG_DIR/_ixh + goloop rpc sendtx call --to $(cat icon.addr.bts) \ + --method setFeeRatio \ + --param _name=$ICON_NATIVE_COIN_NAME \ + --param _feeNumerator=$(decimal2Hex $2) \ + --param _fixedFee=$(decimal2Hex $1) \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 1000000000 \ + --uri $ICON_NET_URI | jq -r . >tx/setICXFee.icon + sleep 3 + ensure_txresult tx/setICXFee.icon +} + +configure_javascore_register_native_token() { + echo "Register Native Token " $2 + cd $CONFIG_DIR/_ixh + #local bts_fee_numerator=100 + #local bts_fixed_fee=5000 + if [ ! -f icon.register.coin$2 ]; then + goloop rpc sendtx call --to $(cat icon.addr.bts) \ + --method register \ + --param _name=$1 \ + --param _symbol=$2 \ + --param _decimals=$(decimal2Hex $5) \ + --param _addr=$ICON_ZERO_ADDRESS \ + --param _feeNumerator=$(decimal2Hex $4) \ + --param _fixedFee=$(decimal2Hex $3) \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 4000000000 \ + --uri $ICON_NET_URI | jq -r . >tx/register.coin.$2 + sleep 5 + ensure_txresult tx/register.coin.$2 + echo "registered "$2 > icon.register.coin$2 + fi +} + + + + +configure_javascore_addLink() { + echo "BMC: Add Link to BSC BMC:" + cd $CONFIG_DIR/_ixh + if [ ! -f icon.configure.addLink ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method addLink \ + --param _link=$(cat tz.addr.bmcperipherybtp) \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 1000000000 \ + --uri $ICON_NET_URI | jq -r . > addLink.icon + + sleep 3 + echo "addedLink" > icon.configure.addLink + fi +} + +configure_javascore_setLinkHeight() { + echo "BMC: SetLinkHeight" + cd $CONFIG_DIR/_ixh + if [ ! -f icon.configure.setLink ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method setLinkRxHeight \ + --param _link=$(cat tz.addr.bmcperipherybtp) \ + --param _height=$(cat tz.chain.height) \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 1000000000 \ + --uri $ICON_NET_URI | jq -r . > setLinkRxHeight.icon + + sleep 3 + echo "setLink" > icon.configure.setLink + fi +} + +configure_bmc_javascore_addRelay() { + echo "Adding bsc Relay" + local icon_bmr_owner=$(cat $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.json | jq -r .address) + echo $icon_bmr_owner + sleep 5 + echo "Starting" + cd $CONFIG_DIR/_ixh + if [ ! -f icon.configure.addRelay ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method addRelay \ + --param _link=$(cat tz.addr.bmcperipherybtp) \ + --param _addr=${icon_bmr_owner} \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 1000000000 \ + --uri $ICON_NET_URI | jq -r . > addRelay.icon + + sleep 3 + echo "addRelay" > icon.configure.addRelay + fi +} + +decimal2Hex() { + hex=$(echo "obase=16; ibase=10; ${@}" | bc) + echo "0x$(tr [A-Z] [a-z] <<< "$hex")" +} + + +configure_relay_config() { + jq -n ' + .base_dir = $base_dir | + .log_level = "debug" | + .console_level = "trace" | + .log_writer.filename = $log_writer_filename | + .relays = [ $b2i_relay, $i2b_relay ]' \ + --arg base_dir "bmr" \ + --arg log_writer_filename "bmr/bmr.log" \ + --argjson b2i_relay "$( + jq -n ' + .name = "t2i" | + .src.address = $src_address | + .src.endpoint = [ $src_endpoint ] | + .src.options.verifier.blockHeight = $src_options_verifier_blockHeight | + .src.options.syncConcurrency = 100 | + .src.options.bmcManagement = $bmc_management | + .src.offset = $src_offset | + .dst.address = $dst_address | + .dst.endpoint = [ $dst_endpoint ] | + .dst.options = $dst_options | + .dst.key_store = $dst_key_store | + .dst.key_store.coinType = $dst_key_store_cointype | + .dst.key_password = $dst_key_password ' \ + --arg src_address "$(cat $CONFIG_DIR/_ixh/tz.addr.bmcperipherybtp)" \ + --arg src_endpoint "$TZ_NET_URI" \ + --argjson src_offset $(cat $CONFIG_DIR/_ixh/tz.chain.height) \ + --argjson src_options_verifier_blockHeight $(cat $CONFIG_DIR/_ixh/tz.chain.height) \ + --arg bmc_management "$(cat $CONFIG_DIR/_ixh/tz.addr.bmc_management)" \ + --arg dst_address "$(cat $CONFIG_DIR/_ixh/icon.addr.bmcbtp)" \ + --arg dst_endpoint "$ICON_NET_URI" \ + --argfile dst_key_store "$CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.json" \ + --arg dst_key_store_cointype "icx" \ + --arg dst_key_password "$(cat $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.secret)" \ + --argjson dst_options '{"step_limit":2500000000, "tx_data_size_limit":8192,"balance_threshold":"10000000000000000000"}' + )" \ + --argjson i2b_relay "$( + jq -n ' + .name = "i2t" | + .src.address = $src_address | + .src.endpoint = [ $src_endpoint ] | + .src.offset = $src_offset | + .src.options.verifier.blockHeight = $src_options_verifier_blockHeight | + .src.options.verifier.validatorsHash = $src_options_verifier_validatorsHash | + .src.options.syncConcurrency = 100 | + .dst.address = $dst_address | + .dst.endpoint = [ $dst_endpoint ] | + .dst.options = $dst_options | + .dst.options.bmcManagement = $bmc_management | + .dst.tx_data_size_limit = $dst_tx_data_size_limit | + .dst.key_store.address = $dst_key_store | + .dst.key_store.coinType = $dst_key_store_cointype | + .dst.key_store.crypto.cipher = $secret | + .dst.key_password = $dst_key_password ' \ + --arg src_address "$(cat $CONFIG_DIR/_ixh/icon.addr.bmcbtp)" \ + --arg src_endpoint "$ICON_NET_URI" \ + --argjson src_offset $(cat $CONFIG_DIR/_ixh/icon.chain.height) \ + --argjson src_options_verifier_blockHeight $(cat $CONFIG_DIR/_ixh/icon.chain.height) \ + --arg src_options_verifier_validatorsHash "$(cat $CONFIG_DIR/_ixh/icon.chain.validators)" \ + --arg dst_address "$(cat $CONFIG_DIR/_ixh/tz.addr.bmcperipherybtp)" \ + --arg dst_endpoint "$TZ_NET_URI" \ + --arg dst_key_store "$(echo $(cat $CONFIG_DIR/_ixh/keystore/tz.bmr.wallet))" \ + --arg dst_key_store_cointype "xtz" \ + --arg secret "$(echo $(cat $CONFIG_DIR/_ixh/keystore/tz.bmr.wallet.secret))" \ + --arg dst_key_password "xyz" \ + --argjson dst_tx_data_size_limit 8192 \ + --argjson dst_options '{"gas_limit":24000000,"tx_data_size_limit":8192,"balance_threshold":"100000000000000000000","boost_gas_price":1.0}' \ + --arg bmc_management "$(cat $CONFIG_DIR/_ixh/tz.addr.bmc_management)" + )" +} + +start_relay() { + cd $BASE_DIR/cmd/iconbridge + go run main.go -config $CONFIG_DIR/_ixh/relay.config.json +} + + +ensure_config_dir +ensure_key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret +ensure_key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret +ensure_key_store $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.json $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.secret +ensure_key_store $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.json $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.secret +ensure_tezos_keystore +fund_it_flag + +build_javascores +deploy_javascore_bmc +deploy_javascore_bts $ICON_FIXED_FEE $ICON_NUMERATOR $ICON_DECIMALS +# deploy_javascore_token + +configure_javascore_add_bmc_owner +configure_javascore_add_bts +configure_javascore_add_bts_owner +configure_javascore_bmc_setFeeAggregator +configure_javascore_bts_setICXFee $ICON_FIXED_FEE $ICON_NUMERATOR +# configure_javascore_register_native_token $TZ_NATIVE_COIN_NAME $TZ_COIN_SYMBOL $TZ_FIXED_FEE $TZ_NUMERATOR $TZ_DECIMALS + + + +# # # tezos configuration +deploy_smartpy_bmc_management +deploy_smartpy_bmc_periphery +deploy_smartpy_bts_periphery +deploy_smartpy_bts_core +deploy_smartpy_bts_owner_manager +configure_dotenv +run_tezos_setters + +# # # icon configuration of tezos +configure_javascore_addLink +configure_javascore_setLinkHeight +configure_bmc_javascore_addRelay + + +configure_relay_config > $CONFIG_DIR/_ixh/relay.config.json +# start_relay + + + diff --git a/devnet/docker/icon-tezos/scripts/testnet.i2t.bmr.sh b/devnet/docker/icon-tezos/scripts/testnet.i2t.bmr.sh new file mode 100755 index 000000000..f4dc7e078 --- /dev/null +++ b/devnet/docker/icon-tezos/scripts/testnet.i2t.bmr.sh @@ -0,0 +1,816 @@ +#!/bin/bash +## smarpy service methods - start ### + +# source utils.sh +# source ~/GoProjects/icon-bridge/devnet/docker/icon-bsc/scripts/rpc.sh +# source keystore.sh + +export ICON_NET_ID=0x7 +export ICON_BMC_NID=0x7.icon +export ICON_NET_URI=https://berlin.net.solidwallet.io/api/v3/ +export TEZOS_BMC_NID=NetXnHfVqm9iesp.tezos +export TZ_NET_URI=https://rpc.ghost.tzstats.com/ + +export BASE_DIR=$(echo $(pwd))/../../../.. +export CONFIG_DIR=$BASE_DIR/devnet/docker/icon-tezos +export TEZOS_SETTER=$BASE_DIR/tezos-addresses +export JAVASCORE_DIR=$BASE_DIR/javascore +export SMARTPY_DIR=$BASE_DIR/smartpy +export CONTRACTS_DIR=$BASE_DIR +export TZ_NATIVE_COIN_NAME=btp-$TEZOS_BMC_NID.XTZ +export TZ_COIN_SYMBOL=XTZ +export TZ_FIXED_FEE=0 +export TZ_NUMERATOR=0 +export TZ_DECIMALS=6 +export ICON_NATIVE_COIN_NAME=btp-$ICON_NET_ID.icon-ICX +export ICON_SYMBOL=ICX +export ICON_FIXED_FEE=0 +export ICON_NUMERATOR=0 +export ICON_DECIMALS=18 +export FEE_GATHERING_INTERVAL=43200 +export ICON_ZERO_ADDRESS=cx0000000000000000000000000000000000000000 + +tz_lastBlock() { + octez-client rpc get /chains/main/blocks/head/header +} + +extract_chainHeight() { + cd $CONFIG_DIR/_ixh + local tz_block_height=$(tz_lastBlock | jq -r .level) + echo $tz_block_height > tz.chain.height +} + +ensure_config_dir() { + echo ensuring config dir + cd $CONFIG_DIR + if [ ! -d _ixh ]; then + echo _ixh not found so creating one + mkdir _ixh + fi + if [ ! -d $CONFIG_DIR/_ixh/tx ]; then + echo tx not found so creating one + cd _ixh + mkdir tx + fi +} + +ensure_tezos_keystore(){ + echo "ensuring key store" + cd $CONFIG_DIR/_ixh/keystore + if [ ! -f tz.bmr.wallet ]; then + echo "creating tezos bmr wallet" + octez-client forget address bmr --force + octez-client gen keys bmr + local keystore=$(echo $(octez-client show address bmr -S)) + local keystoreClone=$keystore + keystore_secret=${keystore#*Secret Key: unencrypted:} + keystore_hash=${keystoreClone#*Hash: } + keystore_hash=${keystore_hash%% *} + echo $keystore_hash > tz.bmr.wallet + echo $keystore_secret > tz.bmr.wallet.secret + fi + + # cd $(echo $SMARTPY_DIR/bmc) + # if [ -f .env ]; then + # echo ".env found" + # octez-client forget address bmcOwner --force + # octez-client gen keys bmcOwner + # local keystore=$(echo $(octez-client show address bmcOwner -S)) + # local keystoreClone=$keystore + # keystore_secret=${keystore#*Secret Key: unencrypted:} + # keystore_hash=${keystoreClone#*Hash: } + # keystore_hash=${keystore_hash%% *} + # echo $keystore_hash > tz.bmc.wallet + # echo $keystore_secret > .env + # fi + + # cd $SMARTPY_DIR/bts + # if [ -f .env ]; then + # echo ".env found" + # octez-client forget address btsOwner --force + # octez-client gen keys btsOwner + # local keystore=$(echo $(octez-client show address btsOwner -S)) + # local keystoreClone=$keystore + # keystore_secret=${keystore#*Secret Key: unencrypted:} + # keystore_hash=${keystoreClone#*Hash: } + # keystore_hash=${keystore_hash%% *} + # echo $keystore_hash > tz.bts.wallet + # echo $keystore_secret > .env + # fi + +} + +ensure_key_secret() { + if [ $# -lt 1 ] ; then + echo "Usage: ensure_key_secret SECRET_PATH" + return 1 + fi + local KEY_SECRET=$1 + if [ ! -f "${KEY_SECRET}" ]; then + mkdir -p $(dirname ${KEY_SECRET}) + echo -n $(openssl rand -hex 20) > ${KEY_SECRET} + fi + echo ${KEY_SECRET} +} + +ensure_key_store() { + if [ $# -lt 2 ] ; then + echo "Usage: ensure_key_store KEYSTORE_PATH SECRET_PATH" + return 1 + fi + local KEY_STORE=$1 + local KEY_SECRET=$(ensure_key_secret $2) + if [ ! -f "${KEY_STORE}" ]; then + echo should not reach here + goloop ks gen --out ${KEY_STORE}tmp -p $(cat ${KEY_SECRET}) > /dev/null 2>&1 + cat ${KEY_STORE}tmp | jq -r . > ${KEY_STORE} + rm ${KEY_STORE}tmp + + fi + echo ${KEY_STORE} +} + +fund_it_flag() { + cd $CONFIG_DIR + if [ ! -f fundit.flag ]; then + echo Fund the recently created wallet and run the script once again + echo icon bmc wallet: $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json | jq -r .address) + echo icon bts wallet: $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json | jq -r .address) + echo icon bmr wallet: $(cat $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.json | jq -r .address) + echo icon fa wallet : $(cat $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.json | jq -r .address) + echo tz bmr wallet : $(cat $CONFIG_DIR/_ixh/keystore/tz.bmr.wallet) + + # echo tz bmc wallet : $(cat $SMARTPY_DIR/bmc/tz.bmc.wallet) + # echo tz bts wallet : $(cat $SMARTPY_DIR/bts/tz.bts.wallet) + + echo fund it flag > fundit.flag + exit 0 + fi +} + +deploy_smartpy_bmc_management(){ + cd $(echo $SMARTPY_DIR/bmc) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bmcmanagementbtp ]; then + echo "deploying bmc_management" + extract_chainHeight + cd $SMARTPY_DIR/bmc + npm run compile bmc_management + local deploy=$(npm run deploy bmc_management @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bmc_management + cd $CONFIG_DIR/_ixh + echo "btp://$(echo $TEZOS_BMC_NID)/$(cat tz.addr.bmc_management)" > $CONFIG_DIR/_ixh/tz.addr.bmcmanagementbtp + fi +} + +deploy_smartpy_bmc_periphery(){ + cd $(echo $SMARTPY_DIR/bmc) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bmcperipherybtp ]; then + echo "deploying bmc_periphery" + npm run compile bmc_periphery + local deploy=$(npm run deploy bmc_periphery @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bmc_periphery + cd $CONFIG_DIR/_ixh + echo "btp://$(echo $TEZOS_BMC_NID)/$(cat tz.addr.bmc_periphery)" > $CONFIG_DIR/_ixh/tz.addr.bmcperipherybtp + fi +} + +deploy_smartpy_bts_periphery(){ + cd $(echo $SMARTPY_DIR/bts) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bts_periphery ]; then + echo "deploying bts_periphery" + npm run compile bts_periphery + local deploy=$(npm run deploy bts_periphery @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bts_periphery + fi +} + +deploy_smartpy_bts_core(){ + cd $(echo $SMARTPY_DIR/bts) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bts_core ]; then + echo "deploying bts_core" + npm run compile bts_core + local deploy=$(npm run deploy bts_core @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bts_core + fi +} + +deploy_smartpy_bts_owner_manager(){ + cd $(echo $SMARTPY_DIR/bts) + if [ ! -f $CONFIG_DIR/_ixh/tz.addr.bts_owner_manager ]; then + echo "deploying bts_owner_manager" + npm run compile bts_owner_manager + local deploy=$(npm run deploy bts_owner_manager @GHOSTNET) + sleep 5 + deploy=${deploy#*::} + echo $deploy > $CONFIG_DIR/_ixh/tz.addr.bts_owner_manager + fi +} + +ensure_txresult() { + OLD_SET_OPTS=$(set +o) + set +e + local TX=$1 + + if [ -f "${TX}" ]; then + TX=$(cat ${TX}) + fi + + sleep 2 + RESULT=$(goloop rpc txresult ${TX} --uri $ICON_NET_URI) + RET=$? + echo $RESULT + while [ "$RET" != "0" ] && [ "$(echo $RESULT | grep -E 'Executing|Pending')" == "$RESULT" ]; do + sleep 1 + RESULT=$(goloop rpc txresult ${TX} --rui $ICON_NET_URI) + RET=$? + echo $RESULT + done + eval "${OLD_SET_OPTS}" + set -e + if [ "$RET" != "0" ]; then + echo $RESULT + return $RET + else + STATUS=$(echo $RESULT | jq -r .status) + if [ "$STATUS" == "0x1" ]; then + return 0 + else + echo $RESULT + return 1 + fi + fi +} + +extract_scoreAddress() { + local TX=$1 + local ADDR=$2 + + RESULT=$(ensure_txresult $TX) + RET=$? + + if [ "$RET" != "0" ]; then + echo $RESULT + return $RET + else + SCORE=$(echo $RESULT | jq -r .scoreAddress) + echo $SCORE | tee ${ADDR} + fi +} + + +configure_smartpy_bmc_management_set_bmc_periphery() { + echo "Adding BMC periphery in bmc management" + cd $(echo $CONFIG_DIR/_ixh) + + local bmc_periphery=$(echo $(cat tz.addr.bmc_periphery)) + echo $bmc_periphery + + local bmc_management=$(echo $(cat tz.addr.bmc_management)) + echo $bmc_management + + local ocBR=\'\" + local coBR=\"\' + local arg=$(echo $(echo $ocBR$(echo $bmc_periphery$(echo $coBR)))) + + echo $arg + + # octez-client transfer 0 from bmcOwner to KT1BE6ohnjunYd1C96yPaThwNvFZu4TN8iBy --entrypoint set_bmc_periphery --arg '"KT1JX3Z3AQnf6oDae87Z9mw1g4jhB38tAGQY"' --burn-cap 1 + echo octez-client transfer 0 from bmcOwner to $(echo $bmc_management) --entrypoint set_bmc_periphery --arg $(echo $arg) --burn-cap 1 +} + +configure_dotenv() { + echo "Configuring .env file for running the setter script" + cd $(echo $CONFIG_DIR/_ixh) + local bmc_periphery=$(echo $(cat tz.addr.bmc_periphery)) + local bmc_management=$(echo $(cat tz.addr.bmc_management)) + local bmc_height=$(echo $(cat tz.chain.height)) + local icon_bmc_height=$(echo $(cat icon.chain.height)) + local icon_bmc=$(echo $(cat icon.addr.bmc)) + echo $bmc_periphery + + local bts_core=$(echo $(cat tz.addr.bts_core)) + local bts_owner_manager=$(echo $(cat tz.addr.bts_owner_manager)) + local bts_periphery=$(echo $(cat tz.addr.bts_periphery)) + + cd $SMARTPY_DIR/bmc + local env=$(cat .env) + env=${env#*=} + local secret_deployer=$(echo "secret_deployer=$(echo $env)") + + cd $(echo $TEZOS_SETTER) + go mod tidy + if [ -f .env ]; then + echo ".env exists so removing" + rm .env + fi + touch .env + local output=.env + + + local TZ_NETWORK=$(echo "TZ_NETWORK=$(echo $TEZOS_BMC_NID)") + local ICON_NETWORK=$(echo "ICON_NETWORK=$(echo $ICON_BMC_NID)") + local TEZOS_NATIVE_COIN_NAME=$(echo "TZ_NATIVE_COIN_NAME=btp-$(echo $TEZOS_BMC_NID)-XTZ") + local TEZOS_SYMBOL=$(echo "TZ_SYMBOL=$(echo $TZ_COIN_SYMBOL)") + local TEZ_FIXED_FEE=$(echo "TZ_FIXED_FEE=$(echo $TZ_FIXED_FEE)") + + local TEZ_NUMERATOR=$(echo "TZ_NUMERATOR=$(echo $TZ_NUMERATOR)") + local TEZ_DECIMALS=$(echo "TZ_DECIMALS=$(echo $TZ_DECIMALS)") + local IC_NATIVE_COIN_NAME=$(echo "ICON_NATIVE_COIN_NAME=$(echo $ICON_NATIVE_COIN_NAME)") + + local IC_SYMBOL=$(echo "ICON_SYMBOL=$(echo $ICON_SYMBOL)") + + local IC_FIXED_FEE=$(echo "ICON_FIXED_FEE=$(echo $ICON_FIXED_FEE)") + + local IC_NUMERATOR=$(echo "ICON_NUMERATOR=$(echo $ICON_NUMERATOR)") + local IC_DECIMALS=$(echo "ICON_DECIMALS=$(echo $ICON_DECIMALS)") + + local BMC_PERIPHERY=$(echo "BMC_PERIPHERY=$(echo $bmc_periphery)") + local BMC_MANAGEMENT=$(echo "BMC_MANAGEMENT=$(echo $bmc_management)") + local BMC_HEIGHT=$(echo "bmc_periphery_height=$(echo $bmc_height)") + + local BTS_PERIPHERY=$(echo "BTS_PERIPHERY=$(echo $bts_periphery)") + local BTS_CORE=$(echo "BTS_CORE=$(echo $bts_core)") + local BTS_OWNER_MANAGER=$(echo "BTS_OWNER_MANAGER=$(echo $bts_owner_manager)") + local RELAY_ADDRESS=$(echo "RELAYER_ADDRESS=$(echo $(cat $CONFIG_DIR/_ixh/keystore/tz.bmr.wallet))") + local ICON_BMC=$(echo "ICON_BMC=$(echo $icon_bmc)") + local ICON_RX_HEIGHT=$(echo "ICON_RX_HEIGHT=$(echo $icon_bmc_height)") + local TEZOS_ENDPOINT=$(echo "TEZOS_ENDPOINT=$(echo $TZ_NET_URI)") + + + echo $secret_deployer>>$output + + echo $TZ_NETWORK>>$output + echo $ICON_NETWORK>>$output + echo $TEZOS_NATIVE_COIN_NAME>>$output + echo $TEZOS_SYMBOL>>$output + echo $TEZ_FIXED_FEE>>$output + echo $TEZ_NUMERATOR>>$output + echo $TEZ_DECIMALS>>$output + echo $IC_NATIVE_COIN_NAME>>$output + echo $IC_SYMBOL>>$output + echo $IC_FIXED_FEE>>$output + echo $IC_NUMERATOR>>$output + echo $IC_DECIMALS>>$output + echo $BMC_PERIPHERY>>$output + echo $BMC_MANAGEMENT>>$output + echo $BMC_HEIGHT>>$output + echo $BTS_PERIPHERY>>$output + echo $BTS_CORE>>$output + echo $BTS_OWNER_MANAGER>>$output + echo $RELAY_ADDRESS>>$output + echo $ICON_BMC>>$output + echo $ICON_RX_HEIGHT>>$output + echo $TEZOS_ENDPOINT>>$output +} + +run_tezos_setters(){ + cd $(echo $TEZOS_SETTER) + go run main.go +} + + +# build java scores +build_javascores(){ + echo in java score + cd $JAVASCORE_DIR + ./gradlew bmc:optimizedJar + ./gradlew bts:optimizedJar + ./gradlew irc2Tradeable:optimizedJar + + # irc2-token + if [ ! -f irc2.jar ]; then + git clone https://github.com/icon-project/java-score-examples.git + cd java-score-examples + ./gradlew irc2-token:clean + ./gradlew irc2-token:optimizedJar + cp irc2-token/build/libs/irc2-token-0.9.1-optimized.jar $JAVASCORE_DIR/irc2.jar + rm -rf $JAVASCORE_DIR/java-score-examples + fi + + cd $JAVASCORE_DIR + cp bmc/build/libs/bmc-optimized.jar $JAVASCORE_DIR/bmc.jar + cp bts/build/libs/bts-optimized.jar $JAVASCORE_DIR/bts.jar + cp irc2Tradeable/build/libs/irc2Tradeable-0.1.0-optimized.jar $JAVASCORE_DIR/irc2Tradeable.jar +} + + + +# deploy java scores + +goloop_lastblock() { + goloop rpc lastblock --uri $ICON_NET_URI +} + +extract_chain_height_and_validator() { + cd $CONFIG_DIR/_ixh + local icon_block_height=$(goloop_lastblock | jq -r .height) + echo $icon_block_height > icon.chain.height + + local validator=$(HEIGHT=0x1 URI=$ICON_NET_URI $BASE_DIR/cmd/iconvalidators/./iconvalidators | jq -r .hash) + echo $validator > icon.chain.validators +} + +deploy_javascore_bmc() { + cd $CONFIG_DIR/_ixh + if [ ! -f icon.addr.bmcbtp ]; then + echo "deploying javascore BMC" + extract_chain_height_and_validator + goloop rpc sendtx deploy $CONTRACTS_DIR/javascore/bmc.jar \ + --content_type application/java \ + --param _net=$ICON_BMC_NID \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 4000000000 \ + --uri $ICON_NET_URI | jq -r . >tx/tx.icon.bmc + sleep 5 + echo $(pwd) + extract_scoreAddress tx/tx.icon.bmc icon.addr.bmc + echo "btp://$(echo $ICON_BMC_NID)/$(cat icon.addr.bmc)" >icon.addr.bmcbtp + fi +} + +deploy_javascore_bts() { + echo "deploying javascore bts" + cd $CONFIG_DIR/_ixh + if [ ! -f icon.addr.bts ]; then + #local bts_fee_numerator=100 + #local bts_fixed_fee=5000 + goloop rpc sendtx deploy $CONTRACTS_DIR/javascore/bts.jar \ + --content_type application/java \ + --param _name=$ICON_NATIVE_COIN_NAME \ + --param _bmc=$(cat icon.addr.bmc) \ + --param _decimals=$(decimal2Hex $3) \ + --param _feeNumerator=$(decimal2Hex $2) \ + --param _fixedFee=$(decimal2Hex $1) \ + --param _serializedIrc2=$(xxd -p $CONTRACTS_DIR/javascore/irc2Tradeable.jar | tr -d '\n') \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret)) \ + --step_limit 4000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . > tx/tx.icon.bts + sleep 5 + extract_scoreAddress tx/tx.icon.bts icon.addr.bts + fi +} + +deploy_javascore_token() { + echo "deploying javascore IRC2Token " $2 + cd $CONFIG_DIR/_ixh + if [ ! -f icon.addr.$2 ]; then + goloop rpc sendtx deploy $CONTRACTS_DIR/javascore/irc2.jar \ + --content_type application/java \ + --param _name="$1" \ + --param _symbol=$2 \ + --param _initialSupply="0x5f5e100" \ + --param _decimals="0x12" \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 4000000000 \ + --uri $ICON_NET_URI | jq -r . >tx/tx.icon.$2 + sleep 5 + extract_scoreAddress tx/tx.icon.$2 icon.addr.$2 + fi +} + + +configure_javascore_add_bmc_owner() { + echo "bmc Add Owner" + echo $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json + local icon_bmc_owner=$(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json | jq -r .address) + cd $CONFIG_DIR/_ixh + local is_owner=$(goloop rpc call \ + --to $(cat icon.addr.bmc) \ + --method isOwner \ + --param _addr=$icon_bmc_owner \ + --uri $ICON_NET_URI | jq -r .) + if [ "$is_owner" == "0x0" ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method addOwner \ + --param _addr=$icon_bmc_owner \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . > tx/addbmcowner.icon + sleep 3 + ensure_txresult tx/addbmcowner.icon + fi +} + +configure_javascore_bmc_setFeeAggregator() { + echo "bmc setFeeAggregator" + cd $CONFIG_DIR/_ixh + local FA=$(cat $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.json | jq -r .address) + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method setFeeAggregator \ + --param _addr=${FA} \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . >tx/setFeeAggregator.icon + sleep 3 + ensure_txresult tx/setFeeAggregator.icon + + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method setFeeGatheringTerm \ + --param _value=$FEE_GATHERING_INTERVAL \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . >tx/setFeeGatheringTerm.icon + sleep 3 + ensure_txresult tx/setFeeGatheringTerm.icon +} + +configure_javascore_add_bts() { + echo "bmc add bts" + cd $CONFIG_DIR/_ixh + local hasBTS=$(goloop rpc call \ + --to $(cat icon.addr.bmc) \ + --method getServices \ + --uri $ICON_NET_URI | jq -r .bts) + if [ "$hasBTS" == "null" ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method addService \ + --value 0 \ + --param _addr=$(cat icon.addr.bts) \ + --param _svc="bts" \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . >tx/addService.icon + sleep 3 + ensure_txresult tx/addService.icon + fi + sleep 5 +} + +configure_javascore_add_bts_owner() { + echo "Add bts Owner" + local icon_bts_owner=$(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json | jq -r .address) + cd $CONFIG_DIR/_ixh + local is_owner=$(goloop rpc call \ + --to $(cat icon.addr.bts) \ + --method isOwner \ + --param _addr="$icon_bts_owner" \ + --uri $ICON_NET_URI | jq -r .) + if [ "$is_owner" == "0x0" ]; then + goloop rpc sendtx call --to $(cat icon.addr.bts) \ + --method addOwner \ + --param _addr=$icon_bts_owner \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret)) \ + --step_limit 1000000000 \ + --nid $ICON_NET_ID \ + --uri $ICON_NET_URI | jq -r . >tx/addBtsOwner.icon + sleep 3 + ensure_txresult tx/addBtsOwner.icon + fi +} + +configure_javascore_bts_setICXFee() { + echo "bts set fee" $ICON_SYMBOL + #local bts_fee_numerator=100 + #local bts_fixed_fee=5000 + cd $CONFIG_DIR/_ixh + goloop rpc sendtx call --to $(cat icon.addr.bts) \ + --method setFeeRatio \ + --param _name=$ICON_NATIVE_COIN_NAME \ + --param _feeNumerator=$(decimal2Hex $2) \ + --param _fixedFee=$(decimal2Hex $1) \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 1000000000 \ + --uri $ICON_NET_URI | jq -r . >tx/setICXFee.icon + sleep 3 + ensure_txresult tx/setICXFee.icon +} + +configure_javascore_register_native_token() { + echo "Register Native Token " $2 + cd $CONFIG_DIR/_ixh + #local bts_fee_numerator=100 + #local bts_fixed_fee=5000 + if [ ! -f icon.register.coin$2 ]; then + goloop rpc sendtx call --to $(cat icon.addr.bts) \ + --method register \ + --param _name=$1 \ + --param _symbol=$2 \ + --param _decimals=$(decimal2Hex $5) \ + --param _addr=$ICON_ZERO_ADDRESS \ + --param _feeNumerator=$(decimal2Hex $4) \ + --param _fixedFee=$(decimal2Hex $3) \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 4000000000 \ + --uri $ICON_NET_URI | jq -r . >tx/register.coin.$2 + sleep 5 + ensure_txresult tx/register.coin.$2 + echo "registered "$2 > icon.register.coin$2 + fi +} + + + + +configure_javascore_addLink() { + echo "BMC: Add Link to BSC BMC:" + cd $CONFIG_DIR/_ixh + if [ ! -f icon.configure.addLink ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method addLink \ + --param _link=$(cat tz.addr.bmcperipherybtp) \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 1000000000 \ + --uri $ICON_NET_URI | jq -r . > addLink.icon + + sleep 3 + echo "addedLink" > icon.configure.addLink + fi +} + +configure_javascore_setLinkHeight() { + echo "BMC: SetLinkHeight" + cd $CONFIG_DIR/_ixh + if [ ! -f icon.configure.setLink ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method setLinkRxHeight \ + --param _link=$(cat tz.addr.bmcperipherybtp) \ + --param _height=$(cat tz.chain.height) \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 1000000000 \ + --uri $ICON_NET_URI | jq -r . > setLinkRxHeight.icon + + sleep 3 + echo "setLink" > icon.configure.setLink + fi +} + +configure_bmc_javascore_addRelay() { + echo "Adding bsc Relay" + local icon_bmr_owner=$(cat $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.json | jq -r .address) + echo $icon_bmr_owner + sleep 5 + echo "Starting" + cd $CONFIG_DIR/_ixh + if [ ! -f icon.configure.addRelay ]; then + goloop rpc sendtx call --to $(cat icon.addr.bmc) \ + --method addRelay \ + --param _link=$(cat tz.addr.bmcperipherybtp) \ + --param _addr=${icon_bmr_owner} \ + --key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json \ + --key_password $(echo $(cat $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret)) \ + --nid $ICON_NET_ID \ + --step_limit 1000000000 \ + --uri $ICON_NET_URI | jq -r . > addRelay.icon + + sleep 3 + echo "addRelay" > icon.configure.addRelay + fi +} + +decimal2Hex() { + hex=$(echo "obase=16; ibase=10; ${@}" | bc) + echo "0x$(tr [A-Z] [a-z] <<< "$hex")" +} + + +configure_relay_config() { + jq -n ' + .base_dir = $base_dir | + .log_level = "debug" | + .console_level = "trace" | + .log_writer.filename = $log_writer_filename | + .relays = [ $b2i_relay, $i2b_relay ]' \ + --arg base_dir "bmr" \ + --arg log_writer_filename "bmr/bmr.log" \ + --argjson b2i_relay "$( + jq -n ' + .name = "t2i" | + .src.address = $src_address | + .src.endpoint = [ $src_endpoint ] | + .src.options.verifier.blockHeight = $src_options_verifier_blockHeight | + .src.options.syncConcurrency = 100 | + .src.options.bmcManagement = $bmc_management | + .src.offset = $src_offset | + .dst.address = $dst_address | + .dst.endpoint = [ $dst_endpoint ] | + .dst.options = $dst_options | + .dst.key_store = $dst_key_store | + .dst.key_store.coinType = $dst_key_store_cointype | + .dst.key_password = $dst_key_password ' \ + --arg src_address "$(cat $CONFIG_DIR/_ixh/tz.addr.bmcperipherybtp)" \ + --arg src_endpoint "$TZ_NET_URI" \ + --argjson src_offset $(cat $CONFIG_DIR/_ixh/tz.chain.height) \ + --argjson src_options_verifier_blockHeight $(cat $CONFIG_DIR/_ixh/tz.chain.height) \ + --arg bmc_management "$(cat $CONFIG_DIR/_ixh/tz.addr.bmc_management)" \ + --arg dst_address "$(cat $CONFIG_DIR/_ixh/icon.addr.bmcbtp)" \ + --arg dst_endpoint "$ICON_NET_URI" \ + --argfile dst_key_store "$CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.json" \ + --arg dst_key_store_cointype "icx" \ + --arg dst_key_password "$(cat $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.secret)" \ + --argjson dst_options '{"step_limit":2500000000, "tx_data_size_limit":8192,"balance_threshold":"10000000000000000000"}' + )" \ + --argjson i2b_relay "$( + jq -n ' + .name = "i2t" | + .src.address = $src_address | + .src.endpoint = [ $src_endpoint ] | + .src.offset = $src_offset | + .src.options.verifier.blockHeight = $src_options_verifier_blockHeight | + .src.options.verifier.validatorsHash = $src_options_verifier_validatorsHash | + .src.options.syncConcurrency = 100 | + .dst.address = $dst_address | + .dst.endpoint = [ $dst_endpoint ] | + .dst.options = $dst_options | + .dst.options.bmcManagement = $bmc_management | + .dst.tx_data_size_limit = $dst_tx_data_size_limit | + .dst.key_store.address = $dst_key_store | + .dst.key_store.coinType = $dst_key_store_cointype | + .dst.key_store.crypto.cipher = $secret | + .dst.key_password = $dst_key_password ' \ + --arg src_address "$(cat $CONFIG_DIR/_ixh/icon.addr.bmcbtp)" \ + --arg src_endpoint "$ICON_NET_URI" \ + --argjson src_offset $(cat $CONFIG_DIR/_ixh/icon.chain.height) \ + --argjson src_options_verifier_blockHeight $(cat $CONFIG_DIR/_ixh/icon.chain.height) \ + --arg src_options_verifier_validatorsHash "$(cat $CONFIG_DIR/_ixh/icon.chain.validators)" \ + --arg dst_address "$(cat $CONFIG_DIR/_ixh/tz.addr.bmcperipherybtp)" \ + --arg dst_endpoint "$TZ_NET_URI" \ + --arg dst_key_store "$(echo $(cat $CONFIG_DIR/_ixh/keystore/tz.bmr.wallet))" \ + --arg secret "$(echo $(cat $CONFIG_DIR/_ixh/keystore/tz.bmr.wallet.secret))" \ + --arg dst_key_store_cointype "xtz" \ + --arg dst_key_password "xyz" \ + --argjson dst_tx_data_size_limit 8192 \ + --argjson dst_options '{"gas_limit":24000000,"tx_data_size_limit":8192,"balance_threshold":"100000000000000000000","boost_gas_price":1.0}' \ + --arg bmc_management "$(cat $CONFIG_DIR/_ixh/tz.addr.bmc_management)" + )" +} + +start_relay() { + cd $BASE_DIR/cmd/iconbridge + go run main.go -config $CONFIG_DIR/_ixh/relay.config.json +} + + +ensure_config_dir +ensure_key_store $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.json $CONFIG_DIR/_ixh/keystore/icon.bts.wallet.secret +ensure_key_store $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.json $CONFIG_DIR/_ixh/keystore/icon.bmc.wallet.secret +ensure_key_store $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.json $CONFIG_DIR/_ixh/keystore/icon.bmr.wallet.secret +ensure_key_store $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.json $CONFIG_DIR/_ixh/keystore/icon.fa.wallet.secret +ensure_tezos_keystore +fund_it_flag + +build_javascores +deploy_javascore_bmc +deploy_javascore_bts $ICON_FIXED_FEE $ICON_NUMERATOR $ICON_DECIMALS +# deploy_javascore_token + +configure_javascore_add_bmc_owner +configure_javascore_add_bts +configure_javascore_add_bts_owner +configure_javascore_bmc_setFeeAggregator +configure_javascore_bts_setICXFee $ICON_FIXED_FEE $ICON_NUMERATOR +# configure_javascore_register_native_token $TZ_NATIVE_COIN_NAME $TZ_COIN_SYMBOL $TZ_FIXED_FEE $TZ_NUMERATOR $TZ_DECIMALS + + + +# # # # tezos configuration +deploy_smartpy_bmc_management +deploy_smartpy_bmc_periphery +deploy_smartpy_bts_periphery +deploy_smartpy_bts_core +deploy_smartpy_bts_owner_manager +configure_dotenv +run_tezos_setters + +# # # icon configuration of tezos +configure_javascore_addLink +configure_javascore_setLinkHeight +configure_bmc_javascore_addRelay + + +configure_relay_config > $CONFIG_DIR/_ixh/relay.config.json +start_relay + + + + diff --git a/docs/getting-started.md b/docs/getting-started.md index 08d453ec9..c9ef345ea 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -25,6 +25,7 @@ Get familiar with some terminologies used in icon-bridge [here.](terminologies.m | /docker | Scripts to create docker containers (Some of it Obsolete) | | /javascore | javascore smart contracts | | /solidity | solidity smart contracts | +| /smartpy | tezos smart contracts | >TLDR version of deployment is available [here](#tdlr) diff --git a/docs/icon-tezos/getting-started.md b/docs/icon-tezos/getting-started.md new file mode 100644 index 000000000..f49a472b6 --- /dev/null +++ b/docs/icon-tezos/getting-started.md @@ -0,0 +1,53 @@ +# Getting Started +This section is for deployment of ICON-Tezos bridge in the testnet. This file also documents all the dependencies and prerequisits that are necessary for the bridge. + +## Setting up on ICON +Install `goloop` cli for interacting with the ICON chain. + +``` + https://github.com/icon-project/goloop/blob/master/doc/build.md +``` + +## Setting up on Tezos +Install `octez-client`, cli tool in tezos for interacting with the Tezos chain. + +``` +https://opentezos.com/tezos-basics/cli-and-rpc/ +``` +Once the `octez-client` is all set, you will have to change the network that your `octez-client` is connected to. + +```sh +octez-client --endpoint https://ghostnet.tezos.marigold.dev config update +``` + +## ICON Tezos Bridge Configuration(Testnet) +For the complete deployment, build and running the relay navigate to +```sh +$ cd $BASE_DIR/devnet/docker/icon-tezos/scripts +$ bash testnet.i2t.bmr.sh +``` +When running the script, first wallets are created and we wil have to fund the wallets using the faucet of the respective chains. + +For example you will get a message like this. +``` +Fund the recently created wallet and run the script once again +icon bmc wallet: hxbc77026b7c3823744d5746507ab5bdb570ef9ca3 +icon bts wallet: hx05256b36068144ec4523fd3943966ea018208ddb +icon bmr wallet: hx416d86b01f48ffe425a8a8a5f61182b1c17a339d +icon fa wallet : hxb7d5d680576de816459ad7b4af659886b2b0e4e3 +tz bmr wallet : tz1dxhHuEcZNXoFyX3PX5A8NNpWnJ3MKHDY2 +``` +Fund the icon wallets using the faucet +``` +https://faucet.iconosphere.io/ +``` +Fund the tezos wallets using the faucet +``` +https://faucet.marigold.dev/ +``` + +Rerun the script again and wait for the relay to start. This script will deploy the ICON smart contracts, Tezos smart contracts and register the native coin of the destination chains in its own chain. + +```sh +$ bash testnet.i2t.bmr.sh +``` \ No newline at end of file diff --git a/go.mod b/go.mod index f72ae3e26..2d172c296 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/icon-project/icon-bridge go 1.13 require ( + blockwatch.cc/tzgo v1.17.0 github.com/MuhammedIrfan/testify-mock v0.0.0-20220912121829-185fc90cd1b6 github.com/algorand/go-algorand-sdk v1.22.0 github.com/aws/aws-sdk-go v1.44.76 @@ -34,7 +35,7 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tinylib/msgp v1.1.2 // indirect github.com/vmihailenco/msgpack/v4 v4.3.11 - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d + golang.org/x/crypto v0.10.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) diff --git a/go.sum b/go.sum index c1f357428..4f1c8b77f 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +blockwatch.cc/tzgo v1.17.0 h1:2PTRGFtw//7Szo7B8Xn9sgPzIXnsnbRWRX+9CqaT1sc= +blockwatch.cc/tzgo v1.17.0/go.mod h1:tTgPzOH1pMhQod2sh2/jjOLabdCQegb8FZG23+fv1XE= cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -159,6 +161,7 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e h1:CHPYEbz71w8DqJ7DRIq+MXyCQsdibK08vdcQTY4ufas= github.com/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e/go.mod h1:6Xhs0ZlsRjXLIiSMLKafbZxML/j30pg9Z1priLuha5s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -204,6 +207,9 @@ github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vs github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= @@ -238,6 +244,10 @@ github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/echa/bson v0.0.0-20220430141917-c0fbdf7f8b79 h1:J+/tX7s5mN1aoeQi2ySzix7+zyEhnymkudOxn7VMze4= +github.com/echa/bson v0.0.0-20220430141917-c0fbdf7f8b79/go.mod h1:Ih8Pfj34Z/kOmaLua+KtFWFK3AviGsH5siipj6Gmoa8= +github.com/echa/log v1.2.4 h1:+3+WEqutIBUbASYnuk9zz6HKlm6o8WsFxlOMbA3BcAA= +github.com/echa/log v1.2.4/go.mod h1:KYs5YtFCgL4yHBBqhPmTBhz5ETI1A8q+qbiDPPF1MiM= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= @@ -259,6 +269,9 @@ github.com/evalphobia/logrus_fluent v0.5.4/go.mod h1:hasyj+CXm3BDP1YhFk/rnTcjleg github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fjl/memsize v0.0.0-20180929194037-2a09253e352a/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= @@ -837,8 +850,9 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -851,8 +865,11 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -1242,6 +1259,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1301,8 +1319,9 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1337,8 +1356,10 @@ golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hM golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1381,8 +1402,11 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1399,8 +1423,10 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1469,12 +1495,23 @@ golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1483,8 +1520,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1551,8 +1591,10 @@ golang.org/x/tools v0.0.0-20200729181040-64cdafbe085c/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools/gopls v0.4.4/go.mod h1:zhyGzA+CAtREUwwq/btQxEx2FHnGzDwJvGs5YqdVCbE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1625,6 +1667,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/bson.v2 v2.0.0-20171018101713-d8c8987b8862 h1:l7JQszYQzJc0GspaN+sivv8wScShqfkhS3nsgID8ees= +gopkg.in/bson.v2 v2.0.0-20171018101713-d8c8987b8862/go.mod h1:VN8wuk/3Ksp8lVZ82HHf/MI1FHOBDt5bPK9VZ8DvymM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1640,6 +1684,8 @@ gopkg.in/go-playground/validator.v9 v9.28.0 h1:6pzvnzx1RWaaQiAmv6e1DvCFULRaz5cKo gopkg.in/go-playground/validator.v9 v9.28.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessageCenter.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessageCenter.java index 52051761c..f9664534d 100644 --- a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessageCenter.java +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessageCenter.java @@ -105,7 +105,7 @@ public void setProperties(BMCProperties properties) { @External(readonly = true) public String name() { - return "BTP Message Center"; + return "BTP Message Center-Tezos"; } @External(readonly = true) diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTPTokenService.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTPTokenService.java index c77e07a63..7127f908f 100644 --- a/javascore/bts/src/main/java/foundation/icon/btp/bts/BTPTokenService.java +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/BTPTokenService.java @@ -121,7 +121,7 @@ public BTPTokenService(Address _bmc, String _name, int _decimals, @External(readonly = true) public String name() { - return "BTP Token Service"; + return "BTP Token Service-Tezos"; } /** @@ -289,7 +289,7 @@ public void addBlacklistAddress(String _net, String[] _addresses) { List blacklist = new ArrayList<>(); for (String addr: _addresses) { - addr = lowercase(addr); + addr = trimWhitespace(addr); if (! isUserBlackListed(_net, addr) && addr.length() > 0) { if (_net.equals(net) && !isValidIconAddress(addr)) { continue; @@ -1456,8 +1456,8 @@ private BigInteger increaseSn() { return newSn; } - private String lowercase(String word) { - return word.trim().toLowerCase(); + private String trimWhitespace(String word) { + return word.trim(); } private String[] getLinks() { @@ -1481,4 +1481,4 @@ private void checkUintLimit(BigInteger value) { require(UINT_CAP.compareTo(value) >= 0, "Value cannot exceed uint(256)-1"); } -} +} \ No newline at end of file diff --git a/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistDB.java b/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistDB.java index 4bf84e6f9..ec665841a 100644 --- a/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistDB.java +++ b/javascore/bts/src/main/java/foundation/icon/btp/bts/BlacklistDB.java @@ -46,8 +46,8 @@ public String at(String net, int index) { return null; } - private String lowercase(String user) { - return user.trim().toLowerCase(); + private String trimWhitespace(String user) { + return user.trim(); } public Integer indexOf(String net, String user) { @@ -65,13 +65,13 @@ public Integer indexOf(String net, String user) { public boolean contains(String net, String user) { var a = blacklistIndex.at(net); if (a != null) { - return a.get(lowercase(user)) != null; + return a.get(trimWhitespace(user)) != null; } return false; } public void addToBlacklist(String net, String user) { - user = lowercase(user); + user = trimWhitespace(user); if (!contains(net, user)) { blacklistedUsers.at(net).add(user); int size = length(net); @@ -80,7 +80,7 @@ public void addToBlacklist(String net, String user) { } public String removeFromBlacklist(String net, String user) { - user = lowercase(user); + user = trimWhitespace(user); Integer valueIdx = indexOf(net, user); var netUsers = blacklistedUsers.at(net); var netIndex = blacklistIndex.at(net); diff --git a/javascore/irc2Tradeable/src/main/java/foundation/icon/btp/irc2Tradeable/IRC2Tradeable.java b/javascore/irc2Tradeable/src/main/java/foundation/icon/btp/irc2Tradeable/IRC2Tradeable.java index 4dcb52b90..ddf30b7cc 100644 --- a/javascore/irc2Tradeable/src/main/java/foundation/icon/btp/irc2Tradeable/IRC2Tradeable.java +++ b/javascore/irc2Tradeable/src/main/java/foundation/icon/btp/irc2Tradeable/IRC2Tradeable.java @@ -35,6 +35,7 @@ public class IRC2Tradeable extends IRC2Basic implements OwnerManager { public IRC2Tradeable(String _name, String _symbol, int _decimals) { super(_name, _symbol, _decimals); + addOwner(Context.getOrigin()); } @External @@ -144,4 +145,4 @@ public boolean isOwner(Address _addr) { @EventLog(indexed = 2) protected void Approval(Address owner, Address spender, BigInteger value) {}; -} \ No newline at end of file +} diff --git a/smartpy/bmc/compile.sh b/smartpy/bmc/compile.sh new file mode 100755 index 000000000..6a4c3b170 --- /dev/null +++ b/smartpy/bmc/compile.sh @@ -0,0 +1,77 @@ + +#!/usr/bin/env bash + +set -e -o pipefail + +echo "----------------------------------------" +echo "Compiling contracts ... " +echo "----------------------------------------" + +# Expected location of SmartPy CLI. +SMART_PY_CLI=~/smartpy-cli/SmartPy.sh + +# Build artifact directory. +OUT_DIR=./contracts/build/.contract_build + +# Array of SmartPy files to compile. +# CONTRACTS_ARRAY=(counter) + +# Exit if SmartPy is not installed. +if [ ! -f "$SMART_PY_CLI" ]; then + echo "Fatal: Please install SmartPy CLI at $SMART_PY_CLI" && exit +fi + +function processContract { + CONTRACT_NAME=$1 + OUT_DIR=$2 + CONTRACT_IN="./contracts/src/${CONTRACT_NAME}.py" + CONTRACT_OUT="${CONTRACT_NAME}.json" + STORAGE_OUT="${CONTRACT_NAME}_storage.json" + CONTRACT_COMPILED="${CONTRACT_NAME}/step_000_cont_0_contract.json" + STORAGE_COMPILED="${CONTRACT_NAME}/step_000_cont_0_storage.json" + + echo ">> Processing ${CONTRACT_NAME}" + + # Ensure file exists. + if [ ! -f "$CONTRACT_IN" ]; then + echo "Fatal: $CONTRACT_IN not found. Running from wrong dir?" && exit + fi + + echo ">>> [1 / 3] Testing ${CONTRACT_NAME} ... " + $SMART_PY_CLI test $CONTRACT_IN $OUT_DIR --html + + echo ">>> [2 / 3] Compiling ${CONTRACT_NAME} ..." + $SMART_PY_CLI compile $CONTRACT_IN $OUT_DIR --html + + echo ">>> [3 / 3] Extracting Michelson contract ... " + cp $OUT_DIR/$CONTRACT_COMPILED ./contracts/build/$CONTRACT_OUT + cp $OUT_DIR/$STORAGE_COMPILED ./contracts/build/$STORAGE_OUT + + echo ">>> Michelson contract written to ${CONTRACT_OUT}" +} + +export PYTHONPATH=$PWD + + +echo "> [1 / 2] Unit Testing and Compiling Contracts." +# Use if you want to pass a contract or more as arguments. +for n in $(seq 1 $#); do + processContract $1 $OUT_DIR + shift +done + +# Use if you want to compile all contracts in CONTRACTS_ARRAY. No arguments needed. +# for i in ${!CONTRACTS_ARRAY[@]}; do +# processContract ${CONTRACTS_ARRAY[$i]} $OUT_DIR +# done + +# Remove build artifacts. +echo "> [2 / 2] Cleaning up ..." +rm -rf $OUT_DIR +rm -rf ./contracts/__pycache__ +rm -rf ./__pycache__ + + +echo "> Removed artifacts." + +echo "> Compilation successful." \ No newline at end of file diff --git a/smartpy/bmc/config/config.ts b/smartpy/bmc/config/config.ts new file mode 100644 index 000000000..2dc56ea2d --- /dev/null +++ b/smartpy/bmc/config/config.ts @@ -0,0 +1,20 @@ +// List your config files here + +export const NETWORK = { + GHOSTNET: { + name: "ghostnet", + url: "https://ghostnet.smartpy.io", + }, + KATHMANDUNET: { + name: "kathmandunet", + url: "https://kathmandunet.smartpy.io", + }, + JAKARTANET: { + name: "jakartanet", + url: "https://jakartanet.smartpy.io", + }, + MAINNET: { + name: "mainnet", + url: "https://mainnet.smartpy.io", + }, +}; diff --git a/smartpy/bmc/contracts/src/RLP_struct.py b/smartpy/bmc/contracts/src/RLP_struct.py new file mode 100644 index 000000000..e8bd389db --- /dev/null +++ b/smartpy/bmc/contracts/src/RLP_struct.py @@ -0,0 +1,285 @@ +import smartpy as sp + +Utils2 = sp.io.import_script_from_url("https://raw.githubusercontent.com/RomarQ/tezos-sc-utils/main/smartpy/utils.py") +types = sp.io.import_script_from_url("file:./contracts/src/Types.py") + + +class DecodeEncodeLibrary: + + def decode_bmc_message(self, rlp): + sp.set_type(rlp, sp.TBytes) + + rlp_bmc_message = sp.local("rlp_decode_bmc_message", sp.map(tkey=sp.TNat)) + is_error = sp.local("error_in_decode_bmc_message", sp.string("Success")) + is_list_lambda = sp.view("is_list", self.data.helper, rlp, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + rlp_bmc_message.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBMCMessageDecoding" + return_value_map = sp.compute(sp.map(tkey=sp.TString, tvalue=sp.TString)) + sn_no = sp.local("sn_no_decode_bmc_message", 0) + decoded_message = sp.local("message_decode_bmc_message", sp.bytes("0x")) + with sp.if_(is_error.value == "Success"): + rlp_ = rlp_bmc_message.value + counter = sp.local("counter", 0) + sp.for k in rlp_.items(): + with sp.if_ (counter.value == 0): + return_value_map["src"] = sp.view("decode_string", self.data.helper, k.value, t=sp.TString).open_some() + with sp.if_ (counter.value == 1): + return_value_map["dst"] = sp.view("decode_string", self.data.helper, k.value, t=sp.TString).open_some() + with sp.if_ (counter.value == 2): + return_value_map["svc"] = sp.view("decode_string", self.data.helper, k.value, t=sp.TString).open_some() + with sp.if_ (counter.value == 3): + sn_in_bytes = sp.view("without_length_prefix", self.data.helper, k.value, t=sp.TBytes).open_some() + _to_int = sp.view("to_int", self.data.helper_parse_negative, sn_in_bytes, t=sp.TInt).open_some() + sn_no.value = _to_int + with sp.if_ (counter.value == 4): + decoded_message.value = sp.view("without_length_prefix", self.data.helper, k.value, + t=sp.TBytes).open_some() + counter.value = counter.value + 1 + return sp.record(bmc_dec_rv = sp.record(src=return_value_map.get("src", default_value = "NoKey"), + dst=return_value_map.get("dst", default_value = "NoKey"), + svc=return_value_map.get("svc", default_value = "NoKey"), + sn=sn_no.value, + message=decoded_message.value), + status = is_error.value) + + def decode_response(self, rlp): + sp.set_type(rlp, sp.TBytes) + + code_num = sp.local("code_decode_response", sp.nat(0)) + rlp_ = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + counter = sp.local("counter_decode_response", 0) + sp.for m in rlp_.items(): + with sp.if_ (counter.value == 0): + code_num.value = Utils2.Int.of_bytes(m.value) + counter.value = counter.value + 1 + + # message in case of error is null which cannot be decoded into string + return sp.record(code=code_num.value, message="Error") + + def decode_init_message(self, rlp): + sp.set_type(rlp, sp.TBytes) + + rlp_init = sp.local("rlp_init_message", sp.map(tkey=sp.TNat)) + rlp_init.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + _links = sp.local("links_init_message", [], sp.TList(sp.TString)) + rlp_ = rlp_init.value + counter = sp.local("counter_init_message", 0) + bytes_message = sp.local("byte_init_message", sp.bytes("0x")) + sp.for g in rlp_.items(): + with sp.if_ (counter.value == 0): + bytes_message.value = g.value + counter.value = counter.value + 1 + + sub_list = sp.local("sub_list_init_message", bytes_message.value) + nsl_init = sp.local("nsl_init_message", sp.map(tkey=sp.TNat)) + nsl_init.value = sp.view("decode_list", self.data.helper, sub_list.value, + t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + new_sub_list = nsl_init.value + + counter.value = 0 + sp.for x in new_sub_list.items(): + _links.value.push(sp.view("decode_string", self.data.helper, x.value, t=sp.TString).open_some()) + counter.value = counter.value + 1 + return sp.record(links_list = _links.value) + + def decode_bmc_service(self, rlp): + sp.set_type(rlp, sp.TBytes) + + rlp_bmc_message = sp.local("rlp_decode_bmc_service", sp.map(tkey=sp.TNat)) + is_error = sp.local("error_in_decode_bmc_service", sp.string("Success")) + is_list_lambda = sp.view("is_list", self.data.helper, rlp, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + rlp_bmc_message.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInDecodingBMCService" + + service_type = sp.local("str_value_decode_bmc_service", "") + payload = sp.local("byt_value_decode_bmc_service", sp.bytes("0x")) + with sp.if_(is_error.value == "Success"): + rlp_ = rlp_bmc_message.value + counter = sp.local("counter_service", 0) + sp.for b in rlp_.items(): + with sp.if_ (counter.value == 0): + service_type.value = sp.view("decode_string", self.data.helper, b.value, t=sp.TString).open_some() + with sp.if_ (counter.value == 1): + payload.value = sp.view("without_length_prefix", self.data.helper, b.value, + t=sp.TBytes).open_some() + counter.value = counter.value + 1 + return sp.record(bmc_service_rv=sp.record(serviceType=service_type.value, + payload=payload.value), status = is_error.value) + + def decode_gather_fee_message(self, rlp): + sp.set_type(rlp, sp.TBytes) + + rlp_gather_message = sp.local("rlp_gather_message", sp.map(tkey=sp.TNat)) + is_error = sp.local("error_in_bmc_fee_message", sp.string("Success")) + is_list_lambda = sp.view("is_list", self.data.helper, rlp, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + rlp_gather_message.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInDecodingFeeMessage" + fa = sp.local("fee_aggregator_gather_message", "") + _svcs = sp.local("_svcs", {}, sp.TMap(sp.TNat, sp.TString)) + with sp.if_(is_error.value == "Success"): + rlp_ = rlp_gather_message.value + byte_message = sp.local("byte_gather_fee", sp.bytes("0x")) + counter = sp.local("counter_gather_message", 0) + sp.for c in rlp_.items(): + with sp.if_ (counter.value == 1): + byte_message.value = c.value + with sp.if_ (counter.value == 0): + fa.value = sp.view("decode_string", self.data.helper, c.value, t=sp.TString).open_some() + counter.value = counter.value + 1 + sub_list = sp.local("sub_list_gather_message", byte_message.value) + new_sub_list_gather = sp.local("new_sub_list_gather_message", sp.map(tkey=sp.TNat)) + is_list_lambda = sp.view("is_list", self.data.helper, sub_list.value, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + new_sub_list_gather.value = sp.view("decode_list", self.data.helper, sub_list.value, + t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInDecodingFeeMessage" + with sp.if_(is_error.value == "Success"): + new_sub_list = new_sub_list_gather.value + + counter.value = 0 + sp.for x in new_sub_list.items(): + _svcs.value[counter.value] = sp.view("decode_string", self.data.helper, x.value, t=sp.TString).open_some() + counter.value = counter.value + 1 + return sp.record(fee_decode_rv = sp.record(fa=fa.value, + svcs=_svcs.value), status = is_error.value) + + def to_message_event(self, rlp): + rlp_message_event = sp.local("rlp_message_event", sp.map(tkey=sp.TNat)) + rlp_message_event.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + next_bmc = sp.local("next_bmc_message_event", "") + seq = sp.local("seq_bmc_message_event", sp.nat(0)) + message = sp.local("message_bmc_message_event", sp.bytes("0x")) + rlp_ = rlp_message_event.value + counter = sp.local("counter_message_event", 0) + sp.for i in rlp_.items(): + with sp.if_ (counter.value == 2): + message.value = sp.view("without_length_prefix", self.data.helper, i.value, t=sp.TBytes).open_some() + with sp.if_ (counter.value == 0): + next_bmc.value = sp.view("decode_string", self.data.helper, i.value, t=sp.TString).open_some() + with sp.if_ (counter.value == 1): + wl_prefix_seq = sp.view("without_length_prefix", self.data.helper, i.value, t=sp.TBytes).open_some() + seq.value = Utils2.Int.of_bytes(wl_prefix_seq) + counter.value = counter.value + 1 + return sp.record(event_rv=sp.record(next_bmc= next_bmc.value, seq= seq.value, message = message.value)) + + def decode_receipt_proof(self, rlp): + rlp_receipt_proof = sp.local("rlp_receipt_proof", sp.map(tkey=sp.TNat)) + without_prefix = sp.view("without_length_prefix", self.data.helper, rlp, t=sp.TBytes).open_some() + rlp_receipt_proof.value = sp.view("decode_list", self.data.helper, without_prefix, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + index = sp.local("index_receipt_proof", 0) + height = sp.local("height_receipt_proof", 0) + events = sp.local("events_receipt_proof", sp.map({}, tkey=sp.TNat, + tvalue=sp.TRecord(next_bmc=sp.TString, + seq=sp.TNat,message=sp.TBytes))) + rlp_ = rlp_receipt_proof.value + byte_message_receipt_proof = sp.local("byte_message_receipt_proof", sp.bytes("0x")) + counter = sp.local("counter", 0) + sp.for i in rlp_.items(): + with sp.if_ (counter.value == 1): + byte_message_receipt_proof.value = sp.view("without_length_prefix", self.data.helper, i.value, t=sp.TBytes).open_some() + with sp.if_ (counter.value == 0): + index.value = Utils2.Int.of_bytes(i.value) + with sp.if_ (counter.value == 2): + wl_prefix = sp.view("without_length_prefix", self.data.helper, i.value, t=sp.TBytes).open_some() + height.value =Utils2.Int.of_bytes(wl_prefix) + counter.value = counter.value + 1 + + sub_list = sp.local("sub_list_receipt_proof", byte_message_receipt_proof.value) + + new_sub_list_receipt_proof = sp.local("new_sub_list_receipt_proof", sp.map(tkey=sp.TNat)) + new_sub_list_receipt_proof.value = sp.view("decode_list", self.data.helper, sub_list.value, + t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + new_sub_list = new_sub_list_receipt_proof.value + counter.value = 0 + + sp.for z in new_sub_list.items(): + from_event = self.to_message_event(z.value) + events.value[counter.value] = from_event.event_rv + counter.value = counter.value + 1 + return sp.record(rv = sp.record(index = index.value, events = events.value, height = height.value)) + + def decode_receipt_proofs(self, rlp): + sp.set_type(rlp, sp.TBytes) + rlp_receipt_proofs = sp.local("rlp_receipt_proofs", sp.map(tkey=sp.TNat)) + rlp_receipt_proofs.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + rlp_ = rlp_receipt_proofs.value + counter = sp.local("counter_receipt_proofs", 0) + receipt_proofs = sp.local("events_receipt_proofs", sp.map({}, tkey=sp.TNat, + tvalue=sp.TRecord(index = sp.TNat, + events = sp.TMap(sp.TNat, sp.TRecord(next_bmc=sp.TString, seq=sp.TNat, message=sp.TBytes)), + height = sp.TNat, + ) + ) + ) + message_byte_receipt_proofs = sp.local("message_byte_receipt_proofs", sp.bytes("0x")) + sp.for i in rlp_.items(): + with sp.if_ (counter.value == 0): + message_byte_receipt_proofs.value = i.value + counter.value = counter.value + 1 + sub_list = sp.local("sub_list_receipt_proofs", message_byte_receipt_proofs.value) + + new_sub_list_receipt_proofs = sp.local("new_sub_list_receipt_proofs", sp.map(tkey=sp.TNat)) + new_sub_list_receipt_proofs.value = sp.view("decode_list", self.data.helper, sub_list.value, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + new_sub_list = new_sub_list_receipt_proofs.value + counter.value = 0 + with sp.if_ (sp.len(new_sub_list) > 0): + sp.for x in new_sub_list.items(): + from_receipt_proofs = self.decode_receipt_proof(x.value) + receipt_proofs.value[counter.value] = from_receipt_proofs.rv + counter.value = counter.value + 1 + return sp.record(receipt_proof = receipt_proofs.value) + + # rlp encoding starts here + + def encode_bmc_service(self, params): + sp.set_type(params, sp.TRecord(serviceType=sp.TString, payload=sp.TBytes)) + + encode_service_type = sp.view("encode_string", self.data.helper, params.serviceType, t=sp.TBytes).open_some() + + payload_rlp = sp.view("encode_list", self.data.helper, [params.payload], t=sp.TBytes).open_some() + payload_rlp = sp.view("with_length_prefix", self.data.helper, payload_rlp, t=sp.TBytes).open_some() + + rlp_bytes_with_prefix = sp.view("encode_list", self.data.helper, [encode_service_type, payload_rlp], + t=sp.TBytes).open_some() + rlp_bytes_with_prefix = sp.view("with_length_prefix", self.data.helper, rlp_bytes_with_prefix, + t=sp.TBytes).open_some() + return rlp_bytes_with_prefix + + def encode_bmc_message(self, params): + sp.set_type(params, sp.TRecord(src=sp.TString, dst=sp.TString, svc=sp.TString, sn=sp.TInt, message=sp.TBytes)) + + rlp = sp.local("rlp_sn", sp.bytes("0x")) + encode_src = sp.view("encode_string", self.data.helper, params.src, t=sp.TBytes).open_some() + encode_dst = sp.view("encode_string", self.data.helper, params.dst, t=sp.TBytes).open_some() + encode_svc = sp.view("encode_string", self.data.helper, params.svc, t=sp.TBytes).open_some() + _to_byte = sp.view("to_byte", self.data.helper_parse_negative, params.sn, t=sp.TBytes).open_some() + rlp.value = _to_byte + + # with sp.if_ (params.sn < sp.int(0)): + encode_sn = sp.view("with_length_prefix", self.data.helper, rlp.value, t=sp.TBytes).open_some() + rlp.value = encode_sn + + rlp_bytes_with_prefix = sp.view("encode_list", self.data.helper, + [encode_src, encode_dst, encode_svc, rlp.value, params.message], + t=sp.TBytes).open_some() + return rlp_bytes_with_prefix + + def encode_response(self, params): + sp.set_type(params, sp.TRecord(code=sp.TNat, message=sp.TString)) + + encode_code = sp.view("encode_nat", self.data.helper, params.code, t=sp.TBytes).open_some() + encode_message = sp.view("encode_string", self.data.helper, params.message, t=sp.TBytes).open_some() + + rlp_bytes_with_prefix = sp.view("encode_list", self.data.helper, [encode_code, encode_message], + t=sp.TBytes).open_some() + final_rlp_bytes_with_prefix = sp.view("with_length_prefix", self.data.helper, rlp_bytes_with_prefix, + t=sp.TBytes).open_some() + return final_rlp_bytes_with_prefix + diff --git a/smartpy/bmc/contracts/src/String.py b/smartpy/bmc/contracts/src/String.py new file mode 100644 index 000000000..d766d7239 --- /dev/null +++ b/smartpy/bmc/contracts/src/String.py @@ -0,0 +1,51 @@ +import smartpy as sp + + + +def split_btp_address(base, prev_string, result_string, list_string, last_string, penultimate_string): + """ + Split the BTP Address format i.e. btp://1234.iconee/0x123456789 + into Network_address (1234.iconee) and Server_address (0x123456789) + :param prev_string: local variable name + :param result_string: local variable name + :param list_string: local variable name + :param last_string: local variable name + :param penultimate_string: local variable name + :param base: String base BTP Address format to be split + :return: The resulting strings of Network_address and Server_address + """ + sp.set_type(base, sp.TString) + + # sep = sp.local("sep", "/") + prev_idx = sp.local(prev_string, 0) + result = sp.local(result_string, []) + sp.for idx in sp.range(0, sp.len(base)): + sp.if sp.slice(base, idx, 1).open_some() == "/": + result.value.push(sp.slice(base, prev_idx.value, sp.as_nat(idx - prev_idx.value)).open_some()) + prev_idx.value = idx + 1 + sp.if sp.len(base) > 0: + result.value.push(sp.slice(base, prev_idx.value, sp.as_nat(sp.len(base) - prev_idx.value)).open_some()) + + inverted_list = sp.local(list_string, result.value) + last = sp.local(last_string, "") + penultimate = sp.local(penultimate_string, "") + + with sp.match_cons(inverted_list.value) as l: + last.value = l.head + inverted_list.value = l.tail + # with sp.else_(): + # sp.failwith("Empty list") + + + with sp.match_cons(inverted_list.value) as l: + penultimate.value = l.head + # with sp.else_(): + # sp.failwith("Only one element") + + return sp.pair(penultimate.value, last.value) + + + + + + diff --git a/smartpy/bmc/contracts/src/Types.py b/smartpy/bmc/contracts/src/Types.py new file mode 100644 index 000000000..49d851278 --- /dev/null +++ b/smartpy/bmc/contracts/src/Types.py @@ -0,0 +1,90 @@ +import smartpy as sp + + +class Types: + + MessageEvent = sp.TRecord( + next_bmc=sp.TString, + seq=sp.TNat, + message=sp.TBytes + ) + + ReceiptProof = sp.TRecord( + index=sp.TNat, + events=sp.TMap(sp.TNat, MessageEvent), + height=sp.TNat + ) + + BMCMessage = sp.TRecord( + src=sp.TString, + dst=sp.TString, + svc=sp.TString, + sn=sp.TInt, + message=sp.TBytes + ) + + MessageEvent = sp.TRecord( + next_bmc=sp.TString, + seq=sp.TNat, + message=sp.TBytes + ) + + Response = sp.TRecord( + code=sp.TNat, + message=sp.TString + ) + + Route = sp.TRecord( + dst=sp.TString, + next=sp.TString + ) + + Link = sp.TRecord( + relays=sp.TSet(sp.TAddress), + reachable=sp.TSet(sp.TString), + rx_seq=sp.TNat, + tx_seq=sp.TNat, + block_interval_src=sp.TNat, + block_interval_dst=sp.TNat, + max_aggregation=sp.TNat, + delay_limit=sp.TNat, + relay_idx=sp.TNat, + rotate_height=sp.TNat, + rx_height=sp.TNat, + rx_height_src=sp.TNat, + is_connected=sp.TBool + ) + + LinkStats = sp.TRecord( + rx_seq=sp.TNat, + tx_seq=sp.TNat, + rx_height=sp.TNat, + current_height=sp.TNat + ) + + BMCService = sp.TRecord( + serviceType=sp.TString, + payload=sp.TBytes + ) + + GatherFeeMessage = sp.TRecord( + fa=sp.TString, + svcs=sp.TMap(sp.TNat, sp.TString) + ) + + RelayStats = sp.TRecord( + addr=sp.TAddress, + block_count=sp.TNat, + msg_count=sp.TNat + ) + + Tuple = sp.TRecord( + prev=sp.TString, + to=sp.TString + ) + + Service = sp.TRecord( + svc=sp.TString, + addr=sp.TAddress + ) + diff --git a/smartpy/bmc/contracts/src/bmc_management.py b/smartpy/bmc/contracts/src/bmc_management.py new file mode 100644 index 000000000..008711061 --- /dev/null +++ b/smartpy/bmc/contracts/src/bmc_management.py @@ -0,0 +1,570 @@ +import smartpy as sp + +types = sp.io.import_script_from_url("file:./contracts/src/Types.py") +strings = sp.io.import_script_from_url("file:./contracts/src/String.py") +rlp = sp.io.import_script_from_url("file:./contracts/src/RLP_struct.py") + + +class BMCManagement(sp.Contract, rlp.DecodeEncodeLibrary): + BLOCK_INTERVAL_MSEC = sp.nat(30000) + LIST_SHORT_START = sp.bytes("0xc0") + ZERO_ADDRESS = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg") + + def __init__(self, owner_address, helper_contract): + self.init( + owners=sp.map(l={owner_address:True}), + bsh_services=sp.map(), + relay_stats=sp.map(), + routes=sp.map(), + links=sp.map(), + list_route_keys=sp.set(), + list_link_names=sp.set(), + bmc_periphery=self.ZERO_ADDRESS, + serial_no=sp.nat(0), + get_route_dst_from_net=sp.map(), + get_link_from_net=sp.map(), + get_link_from_reachable_net=sp.map(), + helper=helper_contract, + is_paused=True + ) + + self.init_type(sp.TRecord( + owners=sp.TMap(sp.TAddress, sp.TBool), + bsh_services=sp.TMap(sp.TString, sp.TAddress), + relay_stats=sp.TMap(sp.TAddress, types.Types.RelayStats), + routes=sp.TMap(sp.TString, sp.TString), + links=sp.TMap(sp.TString, types.Types.Link), + list_route_keys=sp.TSet(sp.TString), + list_link_names=sp.TSet(sp.TString), + bmc_periphery=sp.TAddress, + serial_no=sp.TNat, + get_route_dst_from_net=sp.TMap(sp.TString, sp.TString), + get_link_from_net=sp.TMap(sp.TString, sp.TString), + get_link_from_reachable_net=sp.TMap(sp.TString, types.Types.Tuple), + helper=sp.TAddress, + is_paused=sp.TBool + )) + + def only_owner(self): + with sp.if_(self.data.owners.contains(sp.sender)): + sp.verify(self.data.owners[sp.sender] == True, "Unauthorized") + with sp.else_(): + sp.failwith("Unauthorized") + + def only_bmc_periphery(self): + sp.verify(sp.sender == self.data.bmc_periphery, "Unauthorized") + + @sp.entry_point + def toggle_bridge_on(self): + self.only_owner() + with sp.if_(self.data.is_paused == False): + self.data.is_paused = True + with sp.else_(): + self.data.is_paused = False + + @sp.onchain_view() + def bridge_status(self): + """ + :return: boolean + """ + sp.result(self.data.is_paused) + + @sp.entry_point + def set_helper_address(self, address): + sp.set_type(address, sp.TAddress) + self.only_owner() + self.data.helper = address + + @sp.entry_point + def set_bmc_periphery(self, addr): + """ + :param addr: address of bmc_periphery + :return: + """ + sp.set_type(addr, sp.TAddress) + + self.only_owner() + sp.verify(addr != self.ZERO_ADDRESS, "Invalid Address") + sp.verify(addr != self.data.bmc_periphery, "AlreadyExistsBMCPeriphery") + self.data.bmc_periphery = addr + + @sp.entry_point + def set_bmc_btp_address(self, network): + sp.set_type(network, sp.TString) + + self.only_owner() + # call set_btp_address on BMCPeriphery + set_btp_address_entry_point = sp.contract(sp.TString, + self.data.bmc_periphery, + "set_bmc_btp_address").open_some() + sp.transfer(network, sp.tez(0), set_btp_address_entry_point) + + @sp.entry_point + def add_owner(self, owner): + """ + :param owner: owner address to set + :return: + """ + sp.set_type(owner, sp.TAddress) + + self.only_owner() + sp.verify(self.data.owners.contains(owner) == False, "Already Exists") + self.data.owners[owner] = True + + @sp.entry_point + def remove_owner(self, owner): + """ + + :param owner: owner address to remove + :return: + """ + sp.set_type(owner, sp.TAddress) + + self.only_owner() + sp.verify(sp.len(self.data.owners) > sp.nat(1), "LastOwner") + sp.verify(self.data.owners[owner] == True, "NotExistsPermission") + del self.data.owners[owner] + + @sp.onchain_view() + def is_owner(self, owner): + """ + :param owner: address to check + :return: + """ + sp.set_type(owner, sp.TAddress) + with sp.if_(self.data.owners.contains(owner)): + sp.result(self.data.owners.get(owner)) + with sp.else_(): + sp.result(False) + + @sp.entry_point + def add_service(self, svc, addr): + """ + Add the smart contract for the service. + :param svc: Name of the service + :param addr: Service's contract address + :return: + """ + sp.set_type(svc, sp.TString) + sp.set_type(addr, sp.TAddress) + + self.only_owner() + sp.verify(addr != self.ZERO_ADDRESS, "InvalidAddress") + sp.verify(self.data.bsh_services.contains(svc) == False, "AlreadyExistsBSH") + self.data.bsh_services[svc] = addr + + @sp.entry_point + def remove_service(self, svc): + """ + Unregisters the smart contract for the service. + :param svc: Name of the service + :return: + """ + sp.set_type(svc, sp.TString) + + self.only_owner() + sp.verify(self.data.bsh_services.contains(svc), "NotExistsBSH") + del self.data.bsh_services[svc] + + @sp.onchain_view() + def get_services(self): + """ + Get registered services. + :return: An array of Service. + """ + sp.result(self.data.bsh_services) + + @sp.entry_point(lazify=False) + def update_add_link(self, ep): + self.only_owner() + sp.set_entry_point("add_link", ep) + + @sp.entry_point(lazify=True) + def add_link(self, link): + """ + Initializes status information for the link. + :param link: + :return: BTP Address of connected BMC + """ + sp.set_type(link, sp.TString) + + self.only_owner() + net, addr= sp.match_pair(strings.split_btp_address(link, "prev_idx", "result", "my_list", "last", "penultimate")) + + with sp.if_(self.data.links.contains(link)): + sp.verify(self.data.links.get(link).is_connected == False, "AlreadyExistsLink") + self.data.links[link] = sp.record( + relays=sp.set([]), + reachable=sp.set([]), + rx_seq=sp.nat(0), + tx_seq=sp.nat(0), + block_interval_src=self.BLOCK_INTERVAL_MSEC, + block_interval_dst=sp.nat(0), + max_aggregation=sp.nat(10), + delay_limit=sp.nat(3), + relay_idx=sp.nat(0), + rotate_height=sp.nat(0), + rx_height=sp.nat(0), + rx_height_src=sp.nat(0), + is_connected=True + ) + + self._propagate_internal("Link", link) + links = sp.compute(self.data.list_link_names.elements()) + + self.data.list_link_names.add(link) + self.data.get_link_from_net[net] = link + self._send_internal(link, "Init", links) + + @sp.entry_point(lazify=False) + def update_remove_link(self, ep): + self.only_owner() + sp.set_entry_point("remove_link", ep) + + @sp.entry_point(lazify=True) + def remove_link(self, link): + """ + Removes the link and status information. + :param link: BTP Address of connected BMC + :return: + """ + sp.set_type(link, sp.TString) + + self.only_owner() + with sp.if_(self.data.links.contains(link)): + sp.verify(self.data.links.get(link).is_connected == True, "NotExistsLink") + with sp.else_(): + sp.failwith("NotExistsLink") + + del self.data.links[link] + net, addr= sp.match_pair(strings.split_btp_address(link, "prev_idx", "result", "my_list", "last", "penultimate")) + del self.data.get_link_from_net[net] + self._propagate_internal("Unlink", link) + self.data.list_link_names.remove(link) + + @sp.onchain_view() + def get_links(self): + """ + Get registered links. + :return: An array of links ( BTP Addresses of the BMCs ). + """ + sp.result(self.data.list_link_names.elements()) + + @sp.entry_point + def set_link_rx_height(self, link, height): + """ + :param link: + :param height: + :return: + """ + + sp.set_type(link, sp.TString) + sp.set_type(height, sp.TNat) + + self.only_owner() + with sp.if_(self.data.links.contains(link)): + sp.verify(self.data.links.get(link).is_connected == True, "NotExistsLink") + with sp.else_(): + sp.failwith("NotExistsKey") + sp.verify(height > sp.nat(0), "InvalidRxHeight") + self.data.links[link].rx_height = height + + def _propagate_internal(self, service_type, link): + sp.set_type(service_type, sp.TString) + sp.set_type(link, sp.TString) + + _bytes = sp.bytes("0x") # can be any bytes + rlp_bytes = sp.view("encode_string", self.data.helper, link, t=sp.TBytes).open_some() + rlp_bytes_with_prefix = sp.view("encode_list", self.data.helper, [_bytes, rlp_bytes] , t=sp.TBytes).open_some() + + #encode payload + final_rlp_bytes_with_prefix = sp.view("encode_list", self.data.helper, [rlp_bytes_with_prefix], + t=sp.TBytes).open_some() + sp.for item in self.data.list_link_names.elements(): + with sp.if_(self.data.links.contains(item)): + with sp.if_(self.data.links.get(item).is_connected): + net, addr = sp.match_pair(strings.split_btp_address(item, "prev_idx1", "result1", + "my_list1", "last1", "penultimate1")) + + # call send_message on BMCPeriphery + send_message_args_type = sp.TRecord(to=sp.TString, svc=sp.TString, sn=sp.TInt, msg=sp.TBytes) + send_message_entry_point = sp.contract(send_message_args_type, + self.data.bmc_periphery, + "send_message").open_some() + send_message_args = sp.record(to=net, svc="bmc", sn=sp.int(0), msg=self.encode_bmc_service( + sp.record(serviceType=service_type,payload=final_rlp_bytes_with_prefix))) + sp.transfer(send_message_args, sp.tez(0), send_message_entry_point) + + def _send_internal(self, target, service_type, links): + sp.set_type(target, sp.TString) + sp.set_type(service_type, sp.TString) + sp.set_type(links, sp.TList(sp.TString)) + + rlp_bytes = sp.local("rlp_bytes", sp.bytes("0x")) + with sp.if_(sp.len(links) == sp.nat(0)): + rlp_bytes.value = self.LIST_SHORT_START + with sp.else_(): + sp.for item in links: + _bytes = sp.bytes("0x") # can be any bytes + _rlp_bytes = _bytes + sp.view("encode_string", self.data.helper, item, t=sp.TBytes).open_some() + rlp_bytes.value = sp.view("encode_list", self.data.helper, [rlp_bytes.value, _rlp_bytes], + t=sp.TBytes).open_some() + #encode payload + net, addr = sp.match_pair( + strings.split_btp_address(target, "prev_idx2", "result2", "my_list2", "last2", "penultimate2")) + + # call send_message on BMCPeriphery + send_message_args_type = sp.TRecord(to=sp.TString, svc=sp.TString, sn=sp.TInt, msg=sp.TBytes) + send_message_entry_point = sp.contract(send_message_args_type, + self.data.bmc_periphery, + "send_message").open_some() + send_message_args = sp.record(to=net, svc="bmc", sn=sp.int(0), + msg=self.encode_bmc_service(sp.record(serviceType=service_type, + payload=rlp_bytes.value))) + sp.transfer(send_message_args, sp.tez(0), send_message_entry_point) + + @sp.entry_point(lazify=False) + def update_add_route(self, ep): + self.only_owner() + sp.set_entry_point("add_route", ep) + + @sp.entry_point(lazify=True) + def add_route(self, dst, link): + """ + Add route to the BMC. + :param dst: BTP Address of the destination BMC + :param link: BTP Address of the next BMC for the destination + :return: + """ + sp.set_type(dst, sp.TString) + sp.set_type(link, sp.TString) + + self.only_owner() + sp.verify(self.data.routes.contains(dst) == False, "AlreadyExistRoute") + net, addr= sp.match_pair(strings.split_btp_address(dst, "prev_idx", "result", "my_list", "last", "penultimate")) + strings.split_btp_address(link, "prev_idx1", "result1", "my_list1", "last1", "penultimate1") + + self.data.routes[dst] = link + self.data.list_route_keys.add(dst) + self.data.get_route_dst_from_net[net] = dst + + @sp.entry_point(lazify=False) + def update_remove_route(self, ep): + self.only_owner() + sp.set_entry_point("remove_route", ep) + + @sp.entry_point(lazify=True) + def remove_route(self, dst): + """ + Remove route to the BMC. + :param dst: BTP Address of the destination BMC + :return: + """ + sp.set_type(dst, sp.TString) + + self.only_owner() + sp.verify(self.data.routes.contains(dst) == True, "NotExistRoute") + del self.data.routes[dst] + net, addr= sp.match_pair(strings.split_btp_address(dst, "prev_idx", "result", "my_list", "last", "penultimate")) + del self.data.get_route_dst_from_net[net] + self.data.list_route_keys.remove(dst) + + @sp.onchain_view() + def get_routes(self): + """ + Get routing information. + :return: An array of Route. + """ + + _routes = sp.compute(sp.map(tkey=sp.TNat, tvalue=types.Types.Route)) + i = sp.local("i", sp.nat(0)) + sp.for item in self.data.list_route_keys.elements(): + _routes[i.value] = sp.record(dst=item, next=self.data.routes.get(item)) + i.value += 1 + sp.result(_routes) + + @sp.entry_point(lazify=False) + def update_add_relay(self, ep): + self.only_owner() + sp.set_entry_point("add_relay", ep) + + @sp.entry_point(lazify=True) + def add_relay(self, link, addr): + """ + Registers relay for the network. + :param link: BTP Address of connected BMC + :param addr: the address of Relay + :return: + """ + sp.set_type(link, sp.TString) + sp.set_type(addr, sp.TSet(sp.TAddress)) + + self.only_owner() + sp.verify(self.data.links.contains(link), "NotExistsLink") + sp.verify(self.data.links.get(link).is_connected == True, "NotExistsLink") + self.data.links[link].relays = addr + sp.for item in addr.elements(): + self.data.relay_stats[item] = sp.record(addr=item, block_count=sp.nat(0), msg_count=sp.nat(0)) + + @sp.entry_point(lazify=False) + def update_remove_relay(self, ep): + self.only_owner() + sp.set_entry_point("remove_relay", ep) + + @sp.entry_point(lazify=True) + def remove_relay(self, link, addr): + """ + Unregisters Relay for the network. + :param link: BTP Address of connected BMC + :param addr: the address of Relay + :return: + """ + sp.set_type(link, sp.TString) + sp.set_type(addr, sp.TAddress) + + self.only_owner() + sp.verify(self.data.links.contains(link), "NotExistsLink") + sp.verify((self.data.links.get(link).is_connected == True) & + (sp.len(self.data.links.get(link).relays.elements()) != sp.nat(0)), "Unauthorized") + addr_set = sp.local("addr_set", sp.set(), t=sp.TSet(sp.TAddress)) + sp.for item in self.data.links.get(link).relays.elements(): + with sp.if_(item != addr): + addr_set.value.add(item) + + self.data.links[link].relays = addr_set.value + + @sp.onchain_view() + def get_relays(self, link): + """ + Get registered relays. + :param link: BTP Address of the connected BMC. + :return: A list of relays + """ + sp.set_type(link, sp.TString) + + sp.result(self.data.links.get(link).relays.elements()) + + @sp.onchain_view() + def get_bsh_service_by_name(self, service_name): + sp.set_type(service_name, sp.TString) + sp.result(self.data.bsh_services.get(service_name, + default_value=self.ZERO_ADDRESS)) + + @sp.onchain_view() + def get_link(self, to): + sp.set_type(to, sp.TString) + sp.result(self.data.links.get(to)) + + @sp.onchain_view() + def get_link_rx_seq(self, prev): + sp.set_type(prev, sp.TString) + sp.result(self.data.links.get(prev).rx_seq) + + @sp.onchain_view() + def get_link_tx_seq(self, prev): + sp.set_type(prev, sp.TString) + sp.result(self.data.links.get(prev).tx_seq) + + @sp.onchain_view() + def get_link_rx_height(self, prev): + sp.set_type(prev, sp.TString) + sp.result(self.data.links.get(prev).rx_height) + + @sp.onchain_view() + def get_link_relays(self, prev): + sp.set_type(prev, sp.TString) + sp.result(self.data.links.get(prev).relays.elements()) + + @sp.onchain_view() + def get_relay_status_by_link(self, prev): + sp.set_type(prev, sp.TString) + _relays = sp.compute(sp.map(tkey=sp.TNat, tvalue=types.Types.RelayStats)) + + i = sp.local("i", sp.nat(0)) + sp.for item in self.data.links.get(prev).relays.elements(): + _relays[i.value] = self.data.relay_stats.get(item) + i.value += 1 + sp.result(_relays) + + @sp.entry_point + def update_link_rx_seq(self, prev, val): + sp.set_type(prev, sp.TString) + sp.set_type(val, sp.TNat) + + self.only_bmc_periphery() + self.data.links[prev].rx_seq += val + + @sp.entry_point(lazify=False) + def update_update_link_tx_seq(self, ep): + self.only_owner() + sp.set_entry_point("update_link_tx_seq", ep) + + @sp.entry_point(lazify=True) + def update_link_tx_seq(self, prev, serialized_msg): + sp.set_type(prev, sp.TString) + sp.set_type(serialized_msg, sp.TBytes) + + self.only_bmc_periphery() + self.data.links[prev].tx_seq += sp.nat(1) + + sp.emit(sp.record(next=prev, seq=self.data.links.get(prev).tx_seq, msg=serialized_msg), tag="Message") + + @sp.entry_point + def update_link_rx_height(self, prev, val): + sp.set_type(prev, sp.TString) + sp.set_type(val, sp.TNat) + + self.only_bmc_periphery() + self.data.links[prev].rx_height += val + + @sp.entry_point(lazify=False) + def update_update_link_reachable(self, ep): + self.only_owner() + sp.set_entry_point("update_link_reachable", ep) + + @sp.entry_point(lazify=True) + def update_link_reachable(self, prev, to): + sp.set_type(prev, sp.TString) + sp.set_type(to, sp.TList(sp.TString)) + + self.only_bmc_periphery() + sp.for item in to: + self.data.links[prev].reachable.add(item) + net, addr = sp.match_pair( + strings.split_btp_address(item, "prev_idx", "result", "my_list", "last", "penultimate")) + self.data.get_link_from_reachable_net[net] = sp.record(prev=prev, to=item) + + @sp.entry_point + def update_relay_stats(self, relay, block_count_val, msg_count_val): + sp.set_type(relay, sp.TAddress) + sp.set_type(block_count_val, sp.TNat) + sp.set_type(msg_count_val, sp.TNat) + + self.only_bmc_periphery() + self.data.relay_stats[relay].block_count += block_count_val + self.data.relay_stats[relay].msg_count += msg_count_val + + @sp.onchain_view() + def resolve_route(self, dst_net): + sp.set_type(dst_net, sp.TString) + + dst = sp.local("dst", self.data.get_route_dst_from_net.get(dst_net, default_value=sp.string("")), t=sp.TString) + with sp.if_(sp.len(dst.value)!= sp.nat(0)): + sp.result(sp.pair(self.data.routes.get(dst.value), dst.value)) + with sp.else_(): + dst_link = sp.local("dst_link", self.data.get_link_from_net.get(dst_net, + default_value=sp.string("")), t=sp.TString) + with sp.if_(sp.len(dst_link.value) != sp.nat(0)): + sp.result(sp.pair(dst_link.value, dst_link.value)) + with sp.else_(): + res = sp.local("res", self.data.get_link_from_reachable_net.get(dst_net, default_value= + sp.record(prev="", to="")), t=types.Types.Tuple) + with sp.if_(sp.len(res.value.to) > sp.nat(0)): + sp.result(sp.pair(res.value.prev, res.value.to)) + with sp.else_(): + sp.result(sp.pair("Unreachable", "Unreachable: " + dst_net + " is unreachable")) + + +sp.add_compilation_target("bmc_management", BMCManagement(owner_address=sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP"), + helper_contract=sp.address("KT1HwFJmndBWRn3CLbvhUjdupfEomdykL5a6") + )) diff --git a/smartpy/bmc/contracts/src/bmc_periphery.py b/smartpy/bmc/contracts/src/bmc_periphery.py new file mode 100644 index 000000000..18233270c --- /dev/null +++ b/smartpy/bmc/contracts/src/bmc_periphery.py @@ -0,0 +1,421 @@ +import smartpy as sp + +types = sp.io.import_script_from_url("file:./contracts/src/Types.py") +strings = sp.io.import_script_from_url("file:./contracts/src/String.py") +rlp = sp.io.import_script_from_url("file:./contracts/src/RLP_struct.py") + + +class BMCPreiphery(sp.Contract, rlp.DecodeEncodeLibrary): + BMC_ERR = sp.nat(10) + BSH_ERR = sp.nat(40) + UNKNOWN_ERR = sp.nat(0) + + BMCRevertUnauthorized = sp.string("Unauthorized") + BMCRevertParseFailure = sp.string("ParseFailure") + BMCRevertNotExistsBSH = sp.string("NotExistsBSH") + BMCRevertNotExistsLink = sp.string("NotExistsLink") + BMCRevertInvalidSn = sp.string("InvalidSn") + BMCRevertInvalidSeqNumber = sp.string("InvalidSeqNumber") + BMCRevertNotExistsInternalHandler = sp.string("NotExistsInternalHandler") + BMCRevertUnknownHandleBTPError = sp.string("UnknownHandleBTPError") + BMCRevertUnknownHandleBTPMessage = sp.string("UnknownHandleBTPMessage") + + ZERO_ADDRESS = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg") + + def __init__(self, bmc_management_addr, helper_contract, helper_parse_neg_contract, parse_address): + self.init( + helper=helper_contract, + helper_parse_negative=helper_parse_neg_contract, + bmc_btp_address=sp.string(""), + bmc_management=bmc_management_addr, + parse_contract=parse_address, + ) + + def only_owner(self): + owner = sp.view("is_owner", self.data.bmc_management, sp.sender, t=sp.TBool).open_some() + sp.verify(owner == True, "Unauthorized") + + @sp.entry_point + def set_helper_address(self, address): + sp.set_type(address, sp.TAddress) + self.only_owner() + self.data.helper = address + + @sp.entry_point + def set_parse_negative_addr(self, address): + sp.set_type(address, sp.TAddress) + self.only_owner() + self.data.helper_parse_negative = address + + @sp.entry_point + def set_parse_address(self, address): + sp.set_type(address, sp.TAddress) + self.only_owner() + self.data.parse_contract = address + + @sp.entry_point + def set_bmc_management_addr(self, address): + sp.set_type(address, sp.TAddress) + self.only_owner() + self.data.bmc_management = address + + @sp.entry_point(lazify=False) + def update_set_bmc_btp_address(self, ep): + self.only_owner() + sp.set_entry_point("set_bmc_btp_address", ep) + + @sp.entry_point(lazify=True) + def set_bmc_btp_address(self, network): + sp.set_type(network, sp.TString) + + _self_address = sp.self_address + sp.verify(sp.sender == self.data.bmc_management, "Unauthorized") + with sp.if_(self.data.bmc_btp_address == sp.string("")): + self.data.bmc_btp_address = sp.string("btp://") + network + "/" + \ + sp.view("add_to_str", self.data.parse_contract, _self_address, t=sp.TString).open_some() + with sp.else_(): + sp.failwith("Address already set") + + @sp.onchain_view() + def get_bmc_btp_address(self): + sp.result(self.data.bmc_btp_address) + + @sp.onchain_view() + def get_bmc_management_address(self): + sp.result(self.data.bmc_management) + + def _require_registered_relay(self, prev): + sp.set_type(prev, sp.TString) + + relays = sp.view("get_link_relays", self.data.bmc_management, prev, t=sp.TList(sp.TAddress)).open_some() + check_relay = sp.local("valid_relay_check", False) + sp.for relay in relays: + with sp.if_(sp.sender == relay): + check_relay.value = True + sp.verify(check_relay.value, self.BMCRevertUnauthorized) + + @sp.entry_point(lazify=False) + def update_callback_btp_message(self, ep): + self.only_owner() + sp.set_entry_point("callback_btp_message", ep) + + @sp.entry_point(lazify=False) + def update_callback_btp_error(self, ep): + self.only_owner() + sp.set_entry_point("callback_btp_error", ep) + + @sp.entry_point(lazify=True) + def callback_btp_message(self, string, prev, callback_msg): + sp.set_type(string, sp.TOption(sp.TString)) + sp.set_type(prev, sp.TString) + sp.set_type(callback_msg, types.Types.BMCMessage) + bsh_addr = sp.view("get_bsh_service_by_name", self.data.bmc_management, "bts", t=sp.TAddress).open_some( + "Invalid view") + sp.verify(sp.sender == bsh_addr, "Unauthorized") + + with sp.if_(string.open_some() != "success"): + self._send_error(prev, callback_msg, self.BSH_ERR, self.BMCRevertUnknownHandleBTPMessage) + + @sp.entry_point(lazify=True) + def callback_btp_error(self, string, svc, sn, code, msg): + sp.set_type(string, sp.TOption(sp.TString)) + sp.set_type(svc, sp.TString) + sp.set_type(sn, sp.TInt) + sp.set_type(code, sp.TNat) + sp.set_type(msg, sp.TString) + + bsh_addr = sp.view("get_bsh_service_by_name", self.data.bmc_management, "bts", t=sp.TAddress).open_some( + "Invalid view") + sp.verify(sp.sender == bsh_addr, "Unauthorized") + + with sp.if_(string.open_some() != "success"): + error_code = self.UNKNOWN_ERR + err_msg = self.BMCRevertUnknownHandleBTPError + sp.emit(sp.record(svc=svc, sn=sn, code=code, msg=msg, err_code=error_code, err_msg=err_msg), + tag="ErrorOnBTPError") + + @sp.entry_point(lazify=False) + def update_handle_relay_message(self, ep): + self.only_owner() + sp.set_entry_point("handle_relay_message", ep) + + @sp.entry_point(lazify=True) + def handle_relay_message(self, prev, msg): + sp.set_type(prev, sp.TString) + sp.set_type(msg, sp.TBytes) + + _bridge_pause_status = sp.view("bridge_status", self.data.bmc_management, sp.unit, t=sp.TBool).open_some() + with sp.if_(_bridge_pause_status == True): + sp.failwith("Tezos bridge is paused.") + + with sp.if_(self.data.bmc_btp_address == sp.string("")): + sp.failwith("bmc_btp_address not set") + self._require_registered_relay(prev) + + link_rx_seq = sp.view("get_link_rx_seq", self.data.bmc_management, prev, t=sp.TNat).open_some() + link_rx_height = sp.view("get_link_rx_height", self.data.bmc_management, prev, t=sp.TNat).open_some() + + rx_seq = sp.local("rx_seq", link_rx_seq, t=sp.TNat) + rx_height = sp.local("rx_height", link_rx_height, t=sp.TNat) + # decode rlp message + rps_decode = self.decode_receipt_proofs(msg) + rps = rps_decode.receipt_proof + bmc_msg = sp.local("bmc_msg", sp.record(src="", dst="", svc="", sn=sp.int(0), message=sp.bytes("0x")), + t=types.Types.BMCMessage) + ev = sp.local("ev", sp.record(next_bmc="", seq=sp.nat(0), message=sp.bytes("0x")), + t=types.Types.MessageEvent) + sp.for i in sp.range(sp.nat(0), sp.len(rps)): + with sp.if_(rps[i].height < rx_height.value): + pass + with sp.else_(): + rx_height.value = rps[i].height + sp.for j in sp.range(sp.nat(0), sp.len(rps[i].events)): + #stored events received by decoding in local variable + ev.value = rps[i].events[j] + sp.verify(ev.value.next_bmc == self.data.bmc_btp_address, "Invalid Next BMC") + rx_seq.value += sp.nat(1) + with sp.if_(ev.value.seq < rx_seq.value): + rx_seq.value = sp.as_nat(rx_seq.value-sp.nat(1)) + with sp.else_(): + with sp.if_(ev.value.seq > rx_seq.value): + sp.failwith(self.BMCRevertInvalidSeqNumber) + + _decoded = self.decode_bmc_message(ev.value.message) + bmc_msg.value = _decoded.bmc_dec_rv + with sp.if_(_decoded.status == sp.string("Success")): + with sp.if_(bmc_msg.value.dst == self.data.bmc_btp_address): + self._handle_message(prev, bmc_msg.value) + with sp.else_(): + net, addr = sp.match_pair(strings.split_btp_address(bmc_msg.value.dst, "prev_idx", + "result", "my_list", "last", + "penultimate")) + next_link, prev_link = sp.match_pair(sp.view("resolve_route", + self.data.bmc_management,net, t=sp.TPair(sp.TString, + sp.TString)).open_some("Invalid Call")) + + with sp.if_(next_link != "Unreachable"): + self._send_message(next_link, ev.value.message) + with sp.else_(): + self._send_error(prev, bmc_msg.value, self.BMC_ERR, "Unreachable_"+ net) + + # call update_link_rx_seq on BMCManagement + update_link_rx_seq_args_type = sp.TRecord(prev=sp.TString, val=sp.TNat) + update_link_rx_seq_entry_point = sp.contract(update_link_rx_seq_args_type, + self.data.bmc_management, + "update_link_rx_seq").open_some() + update_link_rx_seq_args = sp.record(prev=prev, val=sp.as_nat(rx_seq.value - link_rx_seq)) + sp.transfer(update_link_rx_seq_args, sp.tez(0), update_link_rx_seq_entry_point) + + # call update_relay_stats on BMCManagement + update_relay_stats_args_type = sp.TRecord(relay=sp.TAddress, block_count_val=sp.TNat, msg_count_val=sp.TNat) + update_relay_stats_entry_point = sp.contract(update_relay_stats_args_type, + self.data.bmc_management, + "update_relay_stats").open_some() + update_relay_stats_args = sp.record(relay=sp.sender, block_count_val=sp.nat(0), + msg_count_val=sp.as_nat(rx_seq.value - link_rx_seq)) + sp.transfer(update_relay_stats_args, sp.tez(0), update_relay_stats_entry_point) + + # call update_link_rx_height on BMCManagement + update_link_rx_height_args_type = sp.TRecord(prev=sp.TString, val=sp.TNat) + update_link_rx_height_entry_point = sp.contract(update_link_rx_height_args_type, + self.data.bmc_management, + "update_link_rx_height").open_some() + update_link_rx_height_args = sp.record(prev=prev, val=sp.as_nat(rx_height.value - link_rx_height)) + sp.transfer(update_link_rx_height_args, sp.tez(0), update_link_rx_height_entry_point) + + + + def _handle_message(self, prev, msg): + sp.set_type(prev, sp.TString) + sp.set_type(msg, types.Types.BMCMessage) + + with sp.if_(msg.svc == "bmc"): + sm = sp.local("sm", sp.record(serviceType="", payload=sp.bytes("0x"))) + _decoded = self.decode_bmc_service(msg.message) + sm.value = _decoded.bmc_service_rv + with sp.if_(_decoded.status != "Success"): + self._send_error(prev, msg, self.BMC_ERR, self.BMCRevertParseFailure) + with sp.else_(): + bool_value = sp.local("bool_value", False) + with sp.if_(sm.value.serviceType == "FeeGathering"): + gather_fee =sp.local("gather_fee", sp.record(fa="__error__", svcs=sp.map({0:""}))) + fee_msg_decoded = self.decode_gather_fee_message(sm.value.payload) + + with sp.if_(fee_msg_decoded.status != "Success"): + self._send_error(prev, msg, self.BMC_ERR, self.BMCRevertParseFailure) + with sp.else_(): + gather_fee.value = fee_msg_decoded.fee_decode_rv + sp.for k in sp.range(sp.nat(0), sp.len(gather_fee.value.svcs)): + bsh_addr = sp.view("get_bsh_service_by_name", self.data.bmc_management, + gather_fee.value.svcs[k], t=sp.TAddress).open_some("Invalid Call") + + with sp.if_(bsh_addr != self.ZERO_ADDRESS): + # call handle_fee_gathering of bts periphery + handle_fee_gathering_args_type = sp.TRecord(fa=sp.TString, + svc=sp.TString) + handle_fee_gathering_entry_point = sp.contract(handle_fee_gathering_args_type, + bsh_addr, + "handle_fee_gathering").open_some() + + handle_fee_gathering_args = sp.record( + fa=gather_fee.value.fa, svc=gather_fee.value.svcs[k]) + sp.transfer(handle_fee_gathering_args, sp.tez(0), handle_fee_gathering_entry_point) + bool_value.value = True + + with sp.if_(sm.value.serviceType == "Init"): + links = self.decode_init_message(sm.value.payload) + # call update_link_reachable on BMCManagement + update_link_reachable_args_type = sp.TRecord(prev=sp.TString, to=sp.TList(sp.TString)) + update_link_reachable_entry_point = sp.contract(update_link_reachable_args_type, + self.data.bmc_management, + "update_link_reachable").open_some() + + update_link_reachable_args = sp.record(prev=prev, to=links.links_list) + sp.transfer(update_link_reachable_args, sp.tez(0), update_link_reachable_entry_point) + bool_value.value = True + + with sp.if_(bool_value.value == False): + sp.failwith(self.BMCRevertNotExistsInternalHandler) + + with sp.else_(): + bsh_addr = sp.view("get_bsh_service_by_name", self.data.bmc_management, msg.svc, + t=sp.TAddress).open_some("Invalid view") + + with sp.if_(bsh_addr == self.ZERO_ADDRESS): + self._send_error(prev, msg, self.BMC_ERR, self.BMCRevertNotExistsBSH) + + with sp.else_(): + with sp.if_(msg.sn >= sp.int(0)): + # strings send in split_btp_address are the name of local variables used in + # split_btp_address which should be unique while calling it multiple times + # in a function. But this works in case of loop as it won't be defined multiple + # as its scope exists throughout the loop + net, addr = sp.match_pair(strings.split_btp_address(msg.src, "prev_idx", "result", + "my_list", "last", "penultimate")) + # implemented callback + # call handle_btp_message on bts periphery + handle_btp_message_args_type = sp.TRecord( + callback=sp.TContract(sp.TRecord(string=sp.TOption(sp.TString), + prev=sp.TString, callback_msg=types.Types.BMCMessage)), + prev=sp.TString, callback_msg=types.Types.BMCMessage, _from=sp.TString, + svc=sp.TString, sn=sp.TInt, msg=sp.TBytes) + + handle_btp_message_entry_point = sp.contract(handle_btp_message_args_type, + bsh_addr, + "handle_btp_message").open_some() + t = sp.TRecord(string=sp.TOption(sp.TString), prev=sp.TString, + callback_msg=types.Types.BMCMessage ) + callback = sp.contract(t, sp.self_address, "callback_btp_message") + + handle_btp_message_args = sp.record(callback=callback.open_some(), + prev=prev, + callback_msg=msg, _from=net, svc=msg.svc, + sn=msg.sn, msg=msg.message) + sp.transfer(handle_btp_message_args, sp.tez(0), handle_btp_message_entry_point) + + with sp.else_(): + res = self.decode_response(msg.message) + # implemented callback + # call handle_btp_error on bts periphery + handle_btp_error_args_type = sp.TRecord( + callback=sp.TContract(sp.TRecord(string=sp.TOption(sp.TString), + svc=sp.TString, sn=sp.TInt, code=sp.TNat, msg=sp.TString)), + svc=sp.TString, sn=sp.TInt, code=sp.TNat, msg=sp.TString) + handle_btp_error_entry_point = sp.contract(handle_btp_error_args_type, + bsh_addr, + "handle_btp_error").open_some() + + t = sp.TRecord(string=sp.TOption(sp.TString), + svc=sp.TString, sn=sp.TInt, code=sp.TNat, msg=sp.TString) + callback = sp.contract(t, sp.self_address, "callback_btp_error") + + handle_btp_error_args = sp.record(callback=callback.open_some(), + svc=msg.svc, sn=msg.sn * -1, code=res.code, msg=res.message) + sp.transfer(handle_btp_error_args, sp.tez(0), handle_btp_error_entry_point) + + def _send_message(self, to ,serialized_msg): + sp.set_type(to, sp.TString) + sp.set_type(serialized_msg, sp.TBytes) + + # call update_link_tx_seq on BMCManagement + update_link_tx_seq_entry_point = sp.contract(sp.TRecord(prev=sp.TString, serialized_msg=sp.TBytes), + self.data.bmc_management, + "update_link_tx_seq").open_some() + sp.transfer(sp.record(prev=to, serialized_msg=serialized_msg), sp.tez(0), update_link_tx_seq_entry_point) + + def _send_error(self, prev, message, err_code, err_msg): + sp.set_type(prev, sp.TString) + sp.set_type(message, types.Types.BMCMessage) + sp.set_type(err_code, sp.TNat) + sp.set_type(err_msg, sp.TString) + + with sp.if_(message.sn > sp.int(0)): + serialized_msg = self.encode_bmc_message(sp.record( + src=self.data.bmc_btp_address, + dst=message.src, + svc=message.svc, + sn=message.sn * -1, + message=self.encode_response(sp.record(code=err_code, message=err_msg)))) + self._send_message(prev, serialized_msg) + + @sp.entry_point(lazify=False) + def update_send_message(self, ep): + self.only_owner() + sp.set_entry_point("send_message", ep) + + @sp.entry_point(lazify=True) + def send_message(self, to, svc, sn, msg): + """ + Send the message to a specific network + :param to: Network Address of destination network + :param svc: Name of the service + :param sn: Serial number of the message, it should be positive + :param msg: Serialized bytes of Service Message + :return: + """ + sp.set_type(to, sp.TString) + sp.set_type(svc, sp.TString) + sp.set_type(sn, sp.TInt) + sp.set_type(msg, sp.TBytes) + + sp.verify((sp.sender == self.data.bmc_management) | + (sp.view("get_bsh_service_by_name", self.data.bmc_management, svc, + t=sp.TAddress).open_some() == sp.sender), + self.BMCRevertUnauthorized) + sp.verify(sn >= sp.int(0), self.BMCRevertInvalidSn) + + next_link, dst = sp.match_pair(sp.view("resolve_route", self.data.bmc_management, + to, t=sp.TPair(sp.TString, sp.TString)).open_some()) + + _rlp = self.encode_bmc_message(sp.record( + src=self.data.bmc_btp_address, + dst=dst, + svc=svc, + sn=sn, + message=msg)) + self._send_message(next_link, _rlp) + + @sp.onchain_view() + def get_status(self, _link): + """ + Get status of BMC + :param _link: BTP Address of the connected BMC + :return: + """ + sp.set_type(_link, sp.TString) + + link = sp.view("get_link", self.data.bmc_management, _link, t=types.Types.Link).open_some() + sp.verify(link.is_connected == True, self.BMCRevertNotExistsLink) + + sp.result(sp.record( + rx_seq=link.rx_seq, + tx_seq=link.tx_seq, + rx_height=link.rx_height, + current_height=sp.level #block height + )) + + +sp.add_compilation_target("bmc_periphery", BMCPreiphery(bmc_management_addr=sp.address("KT1G3R9VqESejtsFnvjHSjzXYfuKuHMeaiE3"), + helper_contract=sp.address("KT1HwFJmndBWRn3CLbvhUjdupfEomdykL5a6"), + helper_parse_neg_contract=sp.address("KT1DHptHqSovffZ7qqvSM9dy6uZZ8juV88gP"), + parse_address=sp.address("KT1VJn3WNXDsyFxeSExjSWKBs9JYqRCJ1LFN") )) diff --git a/smartpy/bmc/contracts/src/check_negative.py b/smartpy/bmc/contracts/src/check_negative.py new file mode 100644 index 000000000..de00342f7 --- /dev/null +++ b/smartpy/bmc/contracts/src/check_negative.py @@ -0,0 +1,27 @@ +import smartpy as sp + + +@sp.module +def main(): + class Convert(sp.Contract): + + @sp.onchain_view() + def check_negative(self, x): + sp.cast(x, sp.bytes) + return sp.to_int(x) < 0 + + @sp.onchain_view() + def to_int(self, x): + sp.cast(x, sp.bytes) + return sp.to_int(x) + + @sp.onchain_view() + def to_byte(self, x): + return sp.to_bytes(x) + + +@sp.add_test(name="test") +def test(): + scenario = sp.test_scenario(main) + c = main.Convert() + scenario += c diff --git a/smartpy/bmc/contracts/src/helper.py b/smartpy/bmc/contracts/src/helper.py new file mode 100644 index 000000000..94d220c85 --- /dev/null +++ b/smartpy/bmc/contracts/src/helper.py @@ -0,0 +1,77 @@ +import smartpy as sp + +Utils = sp.io.import_script_from_url("https://raw.githubusercontent.com/Acurast/acurast-hyperdrive/main/contracts/tezos/libs/utils.py") + + +class Helper(sp.Contract): + def __init__(self): + self.init() + + @sp.onchain_view() + def decode_string(self, params): + sp.set_type(params, sp.TBytes) + decode_string = sp.build_lambda(Utils.RLP.Decoder.decode_string) + sp.result(decode_string(params)) + + @sp.onchain_view() + def prefix_length(self, params): + sp.set_type(params, sp.TBytes) + prefix_length = sp.build_lambda(Utils.RLP.Decoder.prefix_length) + sp.result(prefix_length(params)) + + @sp.onchain_view() + def decode_list(self, params): + sp.set_type(params, sp.TBytes) + decode_list = sp.build_lambda(Utils.RLP.Decoder.decode_list) + sp.result(decode_list(params)) + + @sp.onchain_view() + def is_list(self, params): + sp.set_type(params, sp.TBytes) + is_list = sp.build_lambda(Utils.RLP.Decoder.is_list) + sp.result(is_list(params)) + + @sp.onchain_view() + def of_string(self, params): + sp.set_type(params, sp.TString) + encode_string_packed = sp.build_lambda(Utils.Bytes.of_string) + sp.result(encode_string_packed(params)) + + @sp.onchain_view() + def encode_string(self, params): + sp.set_type(params, sp.TString) + encode_string_packed = sp.build_lambda(Utils.RLP.Encoder.encode_string) + sp.result(encode_string_packed(params)) + + @sp.onchain_view() + def encode_nat(self, params): + sp.set_type(params, sp.TNat) + encode_nat_packed = sp.build_lambda(Utils.RLP.Encoder.encode_nat) + sp.result(encode_nat_packed(params)) + + @sp.onchain_view() + def of_nat(self, params): + sp.set_type(params, sp.TNat) + encode_nat_packed = sp.build_lambda(Utils.Bytes.of_nat) + sp.result(encode_nat_packed(params)) + + @sp.onchain_view() + def with_length_prefix(self, params): + sp.set_type(params, sp.TBytes) + encode_length_packed = sp.build_lambda(Utils.RLP.Encoder.with_length_prefix) + sp.result(encode_length_packed(params)) + + @sp.onchain_view() + def without_length_prefix(self, params): + sp.set_type(params, sp.TBytes) + decode = sp.build_lambda(Utils.RLP.Decoder.without_length_prefix) + sp.result(decode(params)) + + @sp.onchain_view() + def encode_list(self, params): + sp.set_type(params, sp.TList(sp.TBytes)) + encode_list_packed = sp.build_lambda(Utils.RLP.Encoder.encode_list) + sp.result(encode_list_packed(params)) + + +sp.add_compilation_target("helper", Helper()) \ No newline at end of file diff --git a/smartpy/bmc/contracts/tests/BMCManagement_test.py b/smartpy/bmc/contracts/tests/BMCManagement_test.py new file mode 100755 index 000000000..8705a7325 --- /dev/null +++ b/smartpy/bmc/contracts/tests/BMCManagement_test.py @@ -0,0 +1,345 @@ +import smartpy as sp + +BMCManagement = sp.io.import_script_from_url("file:./contracts/src/bmc_management.py") +BMCPeriphery = sp.io.import_script_from_url("file:./contracts/src/bmc_periphery.py") +BMCHelper = sp.io.import_script_from_url("file:./contracts/src/helper.py") +ParseAddress = sp.io.import_script_from_url("file:./contracts/src/parse_address.py") + + + + + +@sp.add_test("BMCManagementTest") +def test(): + sc = sp.test_scenario() + + # test account + alice = sp.test_account("Alice") + creator = sp.test_account("Creator") + jack = sp.test_account("Jack") + bob = sp.test_account("Bob") + creator2 = sp.test_account("creator2") + service1_address = sp.test_account("service1_address") + service2_address = sp.test_account("service2_address") + + + # deploy BMCManagement contract + + helper_contract = deploy_helper_contract() + sc += helper_contract + + bmcManagement_contract = deploy_bmcManagement_contract(creator.address, helper_contract.address) + sc += bmcManagement_contract + + parse_address = deploy_parse_address() + sc += parse_address + + bmcPeriphery_contract = deploy_bmcPeriphery_contract(bmcManagement_contract.address, helper_contract.address, parse_address.address) + sc += bmcPeriphery_contract + + + + bmc_periphery_address = bmcPeriphery_contract.address + + + # Test case 1: bmc_periphery + sc.h1("Test case 1: set bmc_periphery to a valid address") + sc.verify(bmcManagement_contract.data.bmc_periphery.is_some() == False) + bmcManagement_contract.set_bmc_periphery(bmc_periphery_address).run(sender=creator) + # sender should be owner + bmcManagement_contract.set_bmc_periphery(bob.address).run(sender=alice, valid=False, exception="Unauthorized") + # repeated bmc_periphery should throw error + bmcManagement_contract.set_bmc_periphery(bmc_periphery_address).run(sender=creator, valid=False, exception="AlreadyExistsBMCPeriphery") + # Verify that bmc_periphery is set to the valid address + sc.verify(bmcManagement_contract.data.bmc_periphery.is_some() == True) + sc.verify(bmcManagement_contract.data.bmc_periphery.open_some() == bmc_periphery_address) + + # set_bmc_btp_address + bmcManagement_contract.set_bmc_btp_address("tezos.77").run(sender=creator) + + + # Test case 2: add_owner + # throw error when adding owner by random address + bmcManagement_contract.add_owner(alice.address).run(sender=bob, valid=False, exception="Unauthorized") + # successfully added new owner + bmcManagement_contract.add_owner(alice.address).run(sender=creator) + sc.verify(bmcManagement_contract.data.owners[alice.address] == True) + + + # Test case 3: remove owner + # throw error when removing owner by random address + bmcManagement_contract.remove_owner(alice.address).run(sender=bob, valid=False, exception="Unauthorized") + # working + bmcManagement_contract.remove_owner(alice.address).run(sender=creator) + sc.verify(~bmcManagement_contract.data.owners.contains(jack.address)) + + + # Test case 4: is_owner + # Add an owner + bmcManagement_contract.add_owner(creator2.address).run(sender=creator) + # Test the is_owner view function + sc.verify(bmcManagement_contract.is_owner(creator2.address) == True) + + + # Test case 5: add_service function + svc1 = sp.string("service1") + svc2 = sp.string("service2") + svc3 = sp.string("service3") + # add service by random address should fail + bmcManagement_contract.add_service(sp.record(addr=service1_address.address, svc=svc1)).run(sender=bob, valid=False, exception="Unauthorized") + # adding service + bmcManagement_contract.add_service(sp.record(addr=service1_address.address, svc=svc1)).run(sender=creator) + # shouldn't add same service twice + bmcManagement_contract.add_service(sp.record(addr=service1_address.address, svc=svc1)).run(sender=creator, valid=False, exception="AlreadyExistsBSH") + + + # Test case 6: remove_service function + # remove service by random address should fail + bmcManagement_contract.remove_service(svc2).run(sender=bob, valid=False, exception="Unauthorized") + # removing unregistered should throw error + bmcManagement_contract.remove_service(svc3).run(sender=creator, valid=False) + # removing service + bmcManagement_contract.add_service(sp.record(addr=service2_address.address, svc=svc2)).run(sender=creator) + bmcManagement_contract.remove_service(svc2).run(sender=creator) + + + #test case 7: get_services function + services = bmcManagement_contract.get_services() + sc.verify_equal(services, sp.map({0: sp.record(svc=svc1, addr=service1_address.address)})) + + # test case 13: add_route function + dst = "btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258dest" + next_link = "btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b" + # only owner can add routes + bmcManagement_contract.add_route(sp.record(dst=dst, link=next_link)).run(sender=bob, valid=False, exception="Unauthorized") + # should work + bmcManagement_contract.add_route(sp.record(dst=dst, link=next_link)).run(sender=creator) + # cannot add already existed route + bmcManagement_contract.add_route(sp.record(dst=dst, link=next_link)).run(sender=creator, valid=False, exception="AlreadyExistRoute") + + + # # test case 14: remove_route function + # dst1 = "btp://78.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5DEST1" + # next_link1 = "btp://78.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5LINK1" + # # only owner can remove routes + # bmcManagement_contract.remove_route(dst).run(sender=bob, valid=False,exception="Unauthorized") + # # throw error when non-exist route is given & this should throw error but not thrown + # bmcManagement_contract.remove_route(dst1).run(sender=creator, valid=False, exception="NotExistRoute") + # # should work + # bmcManagement_contract.add_route(sp.record(dst=dst1, link=next_link1)).run(sender=creator) + # bmcManagement_contract.remove_route(dst1).run(sender=creator) + + + # # test case 15: get_routes function + # get_routes = bmcManagement_contract.get_routes() + # sc.verify_equal(get_routes, sp.map({0: sp.record(dst=sp.string("btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hDEST"), next=sp.string("btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW"))})) + + + # test case 8: add_link function + # add_link by random address should fail + bmcManagement_contract.add_link("btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW").run(sender=bob, valid=False, exception="Unauthorized") + # should work + bmcManagement_contract.add_link("btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b").run(sender=creator) + # add_link by of same link should fail + bmcManagement_contract.add_link("btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b").run(sender=creator, valid=False, exception="AlreadyExistsLink") + + # + # # test case 9: remove_link function + # # remove_link by random address should fail + # bmcManagement_contract.remove_link("btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnB").run(sender=bob, valid=False, exception="Unauthorized") + # # remove_link should throw error when removing non-existing link + # bmcManagement_contract.remove_link("btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnZ").run(sender=creator, valid=False, exception="NotExistsLink") + # # should work + # bmcManagement_contract.add_link("btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnA").run(sender=creator) + # bmcManagement_contract.remove_link("btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnA").run(sender=creator) + # + # + # # test case 10: get_links function + # link_to_compare = bmcManagement_contract.get_links() + # added_link = sp.list(['btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW']) + # sc.verify_equal(link_to_compare, added_link) + # + # + # # test case 11: set_link_rx_height + # link = sp.string('btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW') + # height = sp.nat(2) + # # error when not exist link is given + # bmcManagement_contract.set_link_rx_height(link="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnA", height=height).run(sender=creator, valid=False, exception="NotExistsKey") + # # error when not invalid height is given + # bmcManagement_contract.set_link_rx_height(link=link, height=sp.nat(0)).run(sender=creator,valid=False,exception="InvalidRxHeight") + # # should work + # bmcManagement_contract.set_link_rx_height(link=link, height=height).run(sender=creator) + # sc.verify_equal(bmcManagement_contract.data.links[link].rx_height, 2) + # + # + # # test case 12: set_link function + # block_interval = sp.nat(2) + # _max_aggregation = sp.nat(3) + # delay_limit = sp.nat(2) + # # only owner should set link + # bmcManagement_contract.set_link(sp.record(_link=link, block_interval=block_interval,_max_aggregation=_max_aggregation, delay_limit=delay_limit)).run(sender=bob, valid=False, exception="Unauthorized") + # # error when link doesnt exist + # bmcManagement_contract.set_link(sp.record(_link="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnZ", block_interval=block_interval,_max_aggregation=_max_aggregation, delay_limit=delay_limit)).run(sender=creator, valid=False, exception="NotExistsLink") + # # error when invalid paramters were given + # bmcManagement_contract.set_link(sp.record(_link=link, block_interval=block_interval,_max_aggregation=sp.nat(0), delay_limit=delay_limit)).run(sender=creator, valid=False, exception="InvalidParam") + # bmcManagement_contract.set_link(sp.record(_link=link, block_interval=block_interval,_max_aggregation=_max_aggregation, delay_limit=sp.nat(0))).run(sender=creator, valid=False, exception="InvalidParam") + # # should work + # bmcManagement_contract.set_link(sp.record(_link=link, block_interval=block_interval,_max_aggregation=_max_aggregation, delay_limit=delay_limit)).run(sender=creator) + # + # + # # test case 16: add_relay function + # # only owner should add relay + # bmcManagement_contract.add_relay(sp.record(link=link, addr=sp.set([sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiADD")]))).run(sender=bob, valid=False, exception="Unauthorized") + # # fail when non-exist link is given + # bmcManagement_contract.add_relay(sp.record(link="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUNONLINK", addr=sp.set([sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiADD")]))).run(sender=creator, valid=False, exception="NotExistsLink") + # # should work + # bmcManagement_contract.add_relay(sp.record(link=link, addr=sp.set([sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")]))).run(sender=creator) + # + # + # # test case 17: remove_relay function + # # only owner should remove relay + # bmcManagement_contract.remove_relay(sp.record(link=link, addr=sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9"))).run(sender=bob, valid=False, exception="Unauthorized") + # # fail when non-exist link is given + # bmcManagement_contract.remove_relay(sp.record(link="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUNONLINK", addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiADD"))).run(sender=creator, valid=False, exception="NotExistsLink") + # # should work + # next_link1 = sp.string("btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5LINK1") + # bmcManagement_contract.add_link(next_link1).run(sender=creator) + # bmcManagement_contract.add_relay(sp.record(link=next_link1, addr=sp.set([sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxADD1")]))).run(sender=creator) + # bmcManagement_contract.remove_relay(sp.record(link=next_link1, addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxADD1"))).run(sender=creator) + # + # + # # test case 18: get_relays function + # compared_to = sp.list([sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")]) + # get_relays = bmcManagement_contract.get_relays(link) + # sc.verify_equal(get_relays, compared_to) + # + # + # # test case 19: get_bsh_service_by_name function + # get_bsh_service_by_name = bmcManagement_contract.get_bsh_service_by_name(svc1) + # sc.verify_equal(get_bsh_service_by_name, service1_address.address) + # + # + # # # test case 20: get_link function + # get_link = bmcManagement_contract.get_link(link) + # data = sp.record( + # relays=sp.set([sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")]), + # reachable=sp.set([]), + # rx_seq=sp.nat(0), + # tx_seq=sp.nat(7), + # block_interval_src=sp.nat(2), + # block_interval_dst=sp.nat(0), + # max_aggregation=sp.nat(3), + # delay_limit=sp.nat(2), + # relay_idx=sp.nat(0), + # rotate_height=sp.nat(0), + # rx_height=sp.nat(0), + # rx_height_src=sp.nat(0), + # is_connected=True + # ) + # sc.verify_equal(get_link, data) + # + # + # # test case 21: get_link_rx_seq function + # get_link_rx_seq = bmcManagement_contract.get_link_rx_seq(link) + # sc.verify_equal(get_link_rx_seq, 0) + # + # + # # test case 22: get_link_tx_seq function + # get_link_tx_seq = bmcManagement_contract.get_link_tx_seq(link) + # sc.verify_equal(get_link_tx_seq, 7) + # + # + # # test case 23: get_link_rx_height function + # get_link_rx_height = bmcManagement_contract.get_link_rx_height(link) + # sc.verify_equal(get_link_rx_height, 0) + # + # + # # test case 24: get_link_relays function + # get_link_relays = bmcManagement_contract.get_link_relays(link) + # sc.verify_equal(get_link_relays, compared_to) + # + # + # # test case 25: get_relay_status_by_link function + # get_link_relays = bmcManagement_contract.get_relay_status_by_link(link) + # sc.verify_equal(get_link_relays, sp.map({0: sp.record(addr=sp.address('tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9'), block_count=0, msg_count=0)})) + # + # + # # test case 26: update_link_rx_seq function + # #only bmc periphery address can run other users should get error + # bmcManagement_contract.update_link_rx_seq(sp.record(prev=next_link1, val=sp.nat(3))).run(sender=creator, valid=False, exception="Unauthorized") + # #working + # bmcManagement_contract.update_link_rx_seq(sp.record(prev=next_link1, val=sp.nat(3))).run(sender=bmc_periphery_address) + # # Check that the value of rx_seq is updated correctly + # sc.verify_equal(bmcManagement_contract.data.links[next_link1].rx_seq, 3) + # + # + # # test case 27: update_link_tx_seq function + # # only bmc periphery address can run other users should get error + # bmcManagement_contract.update_link_tx_seq(next_link1).run(sender=creator, valid=False, exception="Unauthorized") + # # working + # bmcManagement_contract.update_link_tx_seq(next_link1).run(sender=bmc_periphery_address) + # # Check that the value of tx_seq is updated correctly + # sc.verify_equal(bmcManagement_contract.data.links[next_link1].tx_seq, 1) + # + # + # # test case 28: update_link_rx_height function + # #only bmc periphery address can run other users should get error + # bmcManagement_contract.update_link_rx_height(sp.record(prev=next_link1, val=sp.nat(3))).run(sender=creator, valid=False, exception="Unauthorized") + # #working + # bmcManagement_contract.update_link_rx_height(sp.record(prev=next_link1, val=sp.nat(4))).run(sender=bmc_periphery_address) + # # Check that the value of rx_seq is updated correctly + # sc.verify_equal(bmcManagement_contract.data.links[next_link1].rx_height, 4) + # + # + # # test case 29: update_link_reachable function + # to = sp.list(["btp://net1/addr1", "btp://net2/addr2"]) + # # only bmc periphery address can run other users should get error + # bmcManagement_contract.update_link_reachable(sp.record(prev=next_link1, to=to)).run(sender=creator, valid=False, exception="Unauthorized") + # # should work + # bmcManagement_contract.update_link_reachable(sp.record(prev=next_link1, to=to)).run(sender=bmc_periphery_address) + # # value checking + # sc.verify_equal(bmcManagement_contract.data.links[next_link1].reachable, sp.set(['btp://net1/addr1', 'btp://net2/addr2'])) + # + # + # # test case 30: delete_link_reachable function + # #only bmc periphery address can run other users should get error + # bmcManagement_contract.delete_link_reachable(sp.record(prev=next_link1, index=sp.nat(0))).run(sender=creator, valid=False, exception="Unauthorized") + # #working + # bmcManagement_contract.delete_link_reachable(sp.record(prev=next_link1, index=sp.nat(0))).run(sender=bmc_periphery_address) + # # value checking + # sc.verify_equal(bmcManagement_contract.data.links[next_link1].reachable, sp.set(['btp://net2/addr2'])) + # + # + # # test case 31: update_relay_stats function + # #only bmc periphery address can run other users should get error + # bmcManagement_contract.update_relay_stats(sp.record(relay=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiADD"), block_count_val=sp.nat(2), msg_count_val=sp.nat(2))).run(sender=creator, valid=False, exception="Unauthorized") + # #working + # bmcManagement_contract.update_relay_stats(sp.record(relay=sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9"), block_count_val=sp.nat(2), msg_count_val=sp.nat(2))).run(sender=bmc_periphery_address) + # # value checking + # sc.verify_equal(bmcManagement_contract.data.relay_stats[sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")].block_count, 2) + # sc.verify_equal(bmcManagement_contract.data.relay_stats[sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")].msg_count, 2) + # + # + # # test case 32: resolve_route function + # sc.verify_equal(bmcManagement_contract.resolve_route(sp.string('77.tezos')), sp.pair('btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW', 'btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hDEST')) + # + + + +def deploy_bmcManagement_contract(owner, helper): + bmcManagement_contract = BMCManagement.BMCManagement(owner, helper) + return bmcManagement_contract + +def deploy_bmcPeriphery_contract(bmc_addres, helper, parse): + owner = sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9") + bmcPeriphery_contract = BMCPeriphery.BMCPreiphery(bmc_addres, helper, parse, owner) + return bmcPeriphery_contract + +def deploy_helper_contract(): + helper_contract = BMCHelper.Helper() + return helper_contract + + +def deploy_parse_address(): + parse_address = ParseAddress.ParseAddress() + return parse_address \ No newline at end of file diff --git a/smartpy/bmc/contracts/tests/bmc_management_test.py b/smartpy/bmc/contracts/tests/bmc_management_test.py new file mode 100644 index 000000000..c830a6eb9 --- /dev/null +++ b/smartpy/bmc/contracts/tests/bmc_management_test.py @@ -0,0 +1,402 @@ +import smartpy as sp + +BMCManagement = sp.io.import_script_from_url("file:./contracts/src/bmc_management.py") +BMCPeriphery = sp.io.import_script_from_url("file:./contracts/src/bmc_periphery.py") +BMCHelper = sp.io.import_script_from_url("file:./contracts/src/helper.py") +ParseAddress = sp.io.import_script_from_url("file:../bts/contracts/src/parse_address.py") + + +@sp.add_test("BMCManagementTest") +def test(): + sc = sp.test_scenario() + + # test account + alice = sp.test_account("Alice") + creator = sp.test_account("Creator") + jack = sp.test_account("Jack") + bob = sp.test_account("Bob") + bmc_periphery_address = sp.test_account("bmc_periphery_address") + creator2 = sp.test_account("creator2") + service1_address = sp.test_account("service1_address") + service2_address = sp.test_account("service2_address") + ZERO_ADDRESS = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg") + + # deploy BMCManagement contract + helper_contract = deploy_helper_contract() + sc += helper_contract + + bmc_management_contract = deploy_bmc_management_contract(creator.address, helper_contract.address) + sc += bmc_management_contract + + parse_address = deploy_parse_address() + sc += parse_address + + bmc_periphery_address = bmc_periphery_address.address + + # Scenario 1: Contract setters + + # Test cases: + # 1: set_bmc_periphery address + sc.verify(bmc_management_contract.data.bmc_periphery == ZERO_ADDRESS) + bmc_management_contract.set_bmc_periphery(bmc_periphery_address).run(sender=creator) + + # 2: sender non-owner + bmc_management_contract.set_bmc_periphery(bob.address).run(sender=alice, valid=False, exception="Unauthorized") + + # 3: bmc_periphery already set + bmc_management_contract.set_bmc_periphery(bmc_periphery_address).run(sender=creator, valid=False, + exception="AlreadyExistsBMCPeriphery") + # 4: Verify valid bmc_periphery address + sc.verify(bmc_management_contract.data.bmc_periphery != ZERO_ADDRESS) + sc.verify(bmc_management_contract.data.bmc_periphery == bmc_periphery_address) + + # # 5: sender non-owner for set_bmc_btp_address + # bmc_management_contract.set_bmc_btp_address("tezos.77").run(sender=alice, valid=False, exception="Unauthorized") + + # 6: sender is owner for set_bmc_btp_address + bmc_management_contract.set_bmc_btp_address("tezos.77").run(sender=creator) + + # 7 : setting helper address by non-owner + bmc_management_contract.set_helper_address(helper_contract.address).run(sender=jack, valid=False, + exception="Unauthorized") + + # 8 : setting helper address by owner + bmc_management_contract.set_helper_address(helper_contract.address).run(sender=creator) + + # 9: verifying address + sc.verify(bmc_management_contract.data.helper == helper_contract.address) + + # Scenario 2: add / remove owner + + # Test cases: + # 1: set owner by non-owner + bmc_management_contract.add_owner(alice.address).run(sender=bob, valid=False, exception="Unauthorized") + + # 2: set new owner by owner + bmc_management_contract.add_owner(alice.address).run(sender=creator) + sc.verify(bmc_management_contract.data.owners[alice.address] == True) + + # 3: remove owner by non-owner + bmc_management_contract.remove_owner(alice.address).run(sender=bob, valid=False, exception="Unauthorized") + + # 4: remove owner by owner + bmc_management_contract.remove_owner(alice.address).run(sender=creator) + sc.verify(~bmc_management_contract.data.owners.contains(jack.address)) + + # 5: add owner + bmc_management_contract.add_owner(creator2.address).run(sender=creator) + + # 6: verify is_owner + sc.verify(bmc_management_contract.is_owner(creator2.address) == True) + + # Scenario 3: add / remove services and get_services + + # Test cases: + svc1 = sp.string("bmc") + svc2 = sp.string("bts") + # 1: add service by non-owner + bmc_management_contract.add_service( + sp.record(addr=service1_address.address, svc=svc1)).run(sender=bob, valid=False, exception="Unauthorized") + # 2: adding service by owner + bmc_management_contract.add_service(sp.record(addr=service1_address.address, svc=svc1)).run(sender=creator) + + # 3: adding same service + bmc_management_contract.add_service( + sp.record(addr=service1_address.address, svc=svc1)).run(sender=creator, valid=False, + exception="AlreadyExistsBSH") + + # 4: remove service by non-owner + bmc_management_contract.remove_service(svc2).run(sender=bob, valid=False, exception="Unauthorized") + + # 5: remove unregistered service + bmc_management_contract.remove_service(svc2).run(sender=creator, valid=False) + + # 6: removing service + bmc_management_contract.add_service(sp.record(addr=service2_address.address, svc=svc2)).run(sender=creator) + bmc_management_contract.remove_service(svc2).run(sender=creator) + + # 7: verify get_services + services = bmc_management_contract.get_services() + sc.verify_equal(services, sp.map({svc1 : service1_address.address})) + + # Scenario 4: add / remove route and get_routes + + # Test case: + dst = "btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258dest" + next_link = "btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b" + # 1: adding route by non-owner + bmc_management_contract.add_route(sp.record(dst=dst, link=next_link)).run(sender=bob, valid=False, + exception="Unauthorized") + + # 2: adding route by owner + bmc_management_contract.add_route(sp.record(dst=dst, link=next_link)).run(sender=creator) + + # 3: adding same routes + bmc_management_contract.add_route(sp.record(dst=dst, link=next_link)).run(sender=creator, valid=False, + exception="AlreadyExistRoute") + + dst1 = "btp://78.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5DEST1" + next_link1 = "btp://78.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5LINK1" + # 4: removing routes by non-owner + bmc_management_contract.remove_route(dst).run(sender=bob, valid=False, exception="Unauthorized") + + # 5: removing non-exist routes + bmc_management_contract.remove_route(dst1).run(sender=creator, valid=False, exception="NotExistRoute") + + # 6: removing existed routes by owner + bmc_management_contract.add_route(sp.record(dst=dst1, link=next_link1)).run(sender=creator) + bmc_management_contract.remove_route(dst1).run(sender=creator) + + # 7: verify get_routes + get_routes = bmc_management_contract.get_routes() + sc.verify_equal(get_routes, sp.map({0: sp.record( + dst=sp.string("btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258dest"), + next=sp.string("btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b"))})) + + # Scenario 5: add / remove link and get_links + + # Test case: + # 1: adding link by non-owner + bmc_management_contract.add_link( + "btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b").run(sender=bob, valid=False, + exception="Unauthorized") + + # 2: adding link by owner + bmc_management_contract.add_link("btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b").run(sender=creator) + + # 3: adding existed link + bmc_management_contract.add_link( + "btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b").run(sender=creator, valid=False, + exception="AlreadyExistsLink") + + # 4: removing link by non-owner + bmc_management_contract.remove_link( + "btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258dead").run(sender=bob, valid=False, + exception="Unauthorized") + + # 5: removing non-exist link + bmc_management_contract.remove_link( + "btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258dead").run(sender=creator, valid=False, + exception="NotExistsLink") + + # 6: removing existed link by owner + bmc_management_contract.add_link("btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258dead").run(sender=creator) + bmc_management_contract.remove_link("btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258dead").run(sender=creator) + + # 7: verify get_links + link_to_compare = bmc_management_contract.get_links() + added_link = sp.list(['btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b']) + sc.verify_equal(link_to_compare, added_link) + + # Scenario 6: set_link_rx_height + + # Test case: + link = sp.string('btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b') + height = sp.nat(2) + # 1: non-exist link is given + bmc_management_contract.set_link_rx_height(link="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnA", + height=height).run(sender=creator, + valid=False, exception="NotExistsKey") + # 2: invalid height is given + bmc_management_contract.set_link_rx_height(link=link, height=sp.nat(0)).run(sender=creator, valid=False, + exception="InvalidRxHeight") + + # 3: set_link_rx_height by non-owner + bmc_management_contract.set_link_rx_height(link=link, height=height).run(sender=bob, valid=False, + exception="Unauthorized") + + # 4: set_link_rx_height by owner + bmc_management_contract.set_link_rx_height(link=link, height=height).run(sender=creator) + + # 5: verify rx_height value + sc.verify_equal(bmc_management_contract.data.links[link].rx_height, 2) + + # Scenario 8: add / remove relay and get_relays + + # Test case: + # 1: adding relay by non-owner + bmc_management_contract.add_relay( + sp.record(link=link, addr=sp.set( + [sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiADD")]))).run(sender=bob, valid=False, + exception="Unauthorized") + + # 2: adding relay to non-exist link + bmc_management_contract.add_relay(sp.record(link="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUNONLINK", + addr=sp.set([sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiADD")]))).run( + sender=creator, valid=False, exception="NotExistsLink") + + # 3: adding relay by owner + bmc_management_contract.add_relay( + sp.record(link=link, addr=sp.set([sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")]))).run(sender=creator) + + # 4: remove relay by non-owner + bmc_management_contract.remove_relay( + sp.record(link=link, addr=sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9"))).run(sender=bob, valid=False, + exception="Unauthorized") + + # 5: removing relay with non-exist link + bmc_management_contract.remove_relay(sp.record(link="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUNONLINK", + addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiADD"))).run( + sender=creator, valid=False, exception="NotExistsLink") + + # 6: removing relay by owner + next_link1 = sp.string("btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a625link1") + bmc_management_contract.add_link(next_link1).run(sender=creator) + bmc_management_contract.add_relay( + sp.record(link=next_link1, addr=sp.set([sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxADD1")]))).run( + sender=creator) + bmc_management_contract.remove_relay( + sp.record(link=next_link1, addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxADD1"))).run(sender=creator) + + # 7: verify get_relays + compared_to = sp.list([sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")]) + get_relays = bmc_management_contract.get_relays(link) + sc.verify_equal(get_relays, compared_to) + + # Scenario 9: Contract getters + + # Test cases: + # 1: verify get_bsh_service_by_name + get_bsh_service_by_name = bmc_management_contract.get_bsh_service_by_name(svc1) + sc.verify_equal(get_bsh_service_by_name, service1_address.address) + + # 2: verify get_link + get_link = bmc_management_contract.get_link(link) + data = sp.record( + relays=sp.set([sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")]), + reachable=sp.set([]), + rx_seq=sp.nat(0), + tx_seq=sp.nat(0), + block_interval_src=sp.nat(30000), + block_interval_dst=sp.nat(0), + max_aggregation=sp.nat(10), + delay_limit=sp.nat(3), + relay_idx=sp.nat(0), + rotate_height=sp.nat(0), + rx_height=sp.nat(2), + rx_height_src=sp.nat(0), + is_connected=True + ) + sc.verify_equal(get_link, data) + + # 3: verify get_link_rx_seq + get_link_rx_seq = bmc_management_contract.get_link_rx_seq(link) + sc.verify_equal(get_link_rx_seq, 0) + + # 4: verify get_link_tx_seq + get_link_tx_seq = bmc_management_contract.get_link_tx_seq(link) + sc.verify_equal(get_link_tx_seq, 0) + + # 5: verify get_link_rx_height + get_link_rx_height = bmc_management_contract.get_link_rx_height(link) + sc.verify_equal(get_link_rx_height, 2) + + # 6: verify get_link_relays + get_link_relays = bmc_management_contract.get_link_relays(link) + sc.verify_equal(get_link_relays, compared_to) + + # 7: verify get_relay_status_by_link + get_link_relays = bmc_management_contract.get_relay_status_by_link(link) + sc.verify_equal(get_link_relays, sp.map( + {0: sp.record(addr=sp.address('tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9'), block_count=0, msg_count=0)})) + + # Scenario 10: update_link_rx_seq function + + # Test cases: + # 1: update_link_rx_seq by non-owner + bmc_management_contract.update_link_rx_seq( + sp.record(prev=next_link1, val=sp.nat(3))).run(sender=creator, valid=False, exception="Unauthorized") + + # 2: update_link_rx_seq by owner + bmc_management_contract.update_link_rx_seq(sp.record(prev=next_link1, val=sp.nat(3))).run( + sender=bmc_periphery_address) + + # 3: verifying value + sc.verify_equal(bmc_management_contract.data.links[next_link1].rx_seq, 3) + + # Scenario 11: update_link_tx_seq function + + # Test cases: + # 1: update_link_tx_seq by non-bmc_periphery + bmc_management_contract.update_link_tx_seq(sp.record(prev=next_link1, serialized_msg=sp.bytes("0x64"))).run( + sender=creator, valid=False, exception="Unauthorized") + + # 2: update_link_tx_seq by bmc_periphery + bmc_management_contract.update_link_tx_seq(sp.record(prev=next_link1, serialized_msg=sp.bytes("0x64"))).run( + sender=bmc_periphery_address) + + # 3: verifying value + sc.verify_equal(bmc_management_contract.data.links[next_link1].tx_seq, 1) + + # Scenario 12: update_link_rx_height function + + # Test cases: + # 1: update_link_rx_height by non-bmc_periphery + bmc_management_contract.update_link_rx_height( + sp.record(prev=next_link1, val=sp.nat(3))).run(sender=creator, valid=False, exception="Unauthorized") + + # 2: update_link_rx_height by bmc_periphery + bmc_management_contract.update_link_rx_height(sp.record(prev=next_link1, val=sp.nat(4))).run( + sender=bmc_periphery_address) + + # 3: verifying value + sc.verify_equal(bmc_management_contract.data.links[next_link1].rx_height, 4) + + # Scenario 13: update_link_reachable + + # Test cases: + to = sp.list(["btp://net1/addr1", "btp://net2/addr2"]) + # 1: update_link_reachable by non-bmc_periphery + bmc_management_contract.update_link_reachable(sp.record(prev=next_link1, to=to)).run(sender=creator, valid=False, + exception="Unauthorized") + + # 2: update_link_reachable by bmc_periphery + bmc_management_contract.update_link_reachable(sp.record(prev=next_link1, to=to)).run(sender=bmc_periphery_address) + + # 3: verifying value + sc.verify_equal(bmc_management_contract.data.links[next_link1].reachable, + sp.set(['btp://net1/addr1', 'btp://net2/addr2'])) + + # Scenario 13: update_relay_stats and resolve_route function + + # Test cases: + # 1: update_relay_stats by non-bmc_periphery + bmc_management_contract.update_relay_stats( + sp.record(relay=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiADD"), block_count_val=sp.nat(2), + msg_count_val=sp.nat(2))).run(sender=creator, valid=False, exception="Unauthorized") + + # 2: update_relay_stats by bmc_periphery + bmc_management_contract.update_relay_stats( + sp.record(relay=sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9"), block_count_val=sp.nat(2), + msg_count_val=sp.nat(2))).run(sender=bmc_periphery_address) + + # 3: verifying value + sc.verify_equal( + bmc_management_contract.data.relay_stats[sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")].block_count, 2) + sc.verify_equal( + bmc_management_contract.data.relay_stats[sp.address("tz1XGbmLYhqcigxFuBCJrgyJejnwkySE4Sk9")].msg_count, 2) + + # 4: verifying value of resolve_route function + sc.verify_equal(bmc_management_contract.resolve_route(sp.string('0x7.icon')), + sp.pair('btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258d67b', + 'btp://0x7.icon/cxff8a87fde8971a1d10d93dfed3416b0a6258dest')) + + # 4: non-exist link resolve_route + sc.verify_equal(bmc_management_contract.resolve_route(sp.string('0x8.icon')), + sp.pair('Unreachable', + 'Unreachable: 0x8.icon is unreachable')) + + +def deploy_bmc_management_contract(owner, helper): + bmc_management_contract = BMCManagement.BMCManagement(owner, helper) + return bmc_management_contract + + +def deploy_helper_contract(): + helper_contract = BMCHelper.Helper() + return helper_contract + + +def deploy_parse_address(): + parse_address = ParseAddress.ParseAddress() + return parse_address diff --git a/smartpy/bmc/contracts/tests/bmc_periphery_test.py b/smartpy/bmc/contracts/tests/bmc_periphery_test.py new file mode 100644 index 000000000..34d069afd --- /dev/null +++ b/smartpy/bmc/contracts/tests/bmc_periphery_test.py @@ -0,0 +1,99 @@ +import smartpy as sp + +BMCManagement = sp.io.import_script_from_url("file:./contracts/src/bmc_management.py") +BMCPeriphery = sp.io.import_script_from_url("file:./contracts/src/bmc_periphery.py") +BMCHelper = sp.io.import_script_from_url("file:./contracts/src/helper.py") +ParseAddress = sp.io.import_script_from_url("file:../bts/contracts/src/parse_address.py") + + +@sp.add_test("BMCPeripheryTest") +def test(): + sc = sp.test_scenario() + + # test account + alice = sp.test_account("Alice") + jack = sp.test_account("Jack") + helper2 = sp.test_account("Helper2") + owner = sp.test_account("owner") + + helper_contract = deploy_helper_contract() + sc += helper_contract + + # deploy BMCManagement contract + bmc_management_contract = deploy_bmc_management_contract(owner.address, helper_contract.address) + sc += bmc_management_contract + + parse_address = deploy_parse_address() + sc += parse_address + + bmc_periphery_contract = deploy_bmc_periphery_contract( + bmc_management_contract.address, helper_contract.address, helper2.address, parse_address.address) + sc += bmc_periphery_contract + + # Scenario 1: Contract setters + + # Test cases: + # 1: set_bmc_btp_address by non-owner + bmc_periphery_contract.set_bmc_btp_address("tezos.77").run(sender=alice, valid=False, exception="Unauthorized") + + # 2: set_bmc_btp_address by owner + bmc_periphery_contract.set_bmc_btp_address("tezos.77").run(sender=bmc_management_contract.address) + + # 3: verify value for bmc_btp_address + sc.verify(bmc_periphery_contract.data.bmc_btp_address == + sp.string("btp://tezos.77/KT1Tezooo3zzSmartPyzzSTATiCzzzseJjWC")) + + # 4: verify value for get_bmc_btp_address + sc.verify_equal(bmc_periphery_contract.get_bmc_btp_address(), + sp.string("btp://tezos.77/KT1Tezooo3zzSmartPyzzSTATiCzzzseJjWC")) + + # 5: set_helper_address by non-owner + bmc_periphery_contract.set_helper_address(helper_contract.address).run(sender=jack, valid=False, + exception="Unauthorized") + + # 6: set_helper_address by non-owner + bmc_periphery_contract.set_helper_address(helper_contract.address).run(sender=owner.address) + + # 7: verify helper address + sc.verify(bmc_periphery_contract.data.helper == helper_contract.address) + + # 8: set_parse_address by non-owner + bmc_periphery_contract.set_parse_address(parse_address.address).run(sender=jack, valid=False, + exception="Unauthorized") + + # 8: set_parse_address by owner + bmc_periphery_contract.set_parse_address(parse_address.address).run(sender=owner.address) + + # 9: verify parse_contract address + sc.verify(bmc_periphery_contract.data.parse_contract == parse_address.address) + + # 10: set_bmc_management_addr by non-owner + bmc_periphery_contract.set_bmc_management_addr(bmc_management_contract.address).run(sender=jack, valid=False, + exception="Unauthorized") + + # 11: set_bmc_management_addr by owner + bmc_periphery_contract.set_bmc_management_addr(bmc_management_contract.address).run( + sender=owner.address) + + # 9: verifying bmc_management address + sc.verify(bmc_periphery_contract.data.bmc_management == bmc_management_contract.address) + + +def deploy_bmc_management_contract(owner, helper): + bmc_management_contract = BMCManagement.BMCManagement(owner, helper) + return bmc_management_contract + + +def deploy_bmc_periphery_contract(bmc_address, helper, helper2, parse): + bmc_periphery_contract = BMCPeriphery.BMCPreiphery(bmc_address, helper, helper2, parse) + return bmc_periphery_contract + + +def deploy_helper_contract(): + helper_contract = BMCHelper.Helper() + return helper_contract + + +def deploy_parse_address(): + parse_address = ParseAddress.ParseAddress() + return parse_address diff --git a/smartpy/bmc/contracts/tests/fa2_dummy.py b/smartpy/bmc/contracts/tests/fa2_dummy.py new file mode 100644 index 000000000..84d9915d6 --- /dev/null +++ b/smartpy/bmc/contracts/tests/fa2_dummy.py @@ -0,0 +1,33 @@ +import smartpy as sp + +FA2 = sp.io.import_script_from_url("https://legacy.smartpy.io/templates/fa2_lib.py") + + +class SingleAssetToken(FA2.Admin, FA2.Fa2SingleAsset, FA2.MintSingleAsset, FA2.BurnSingleAsset, + FA2.OnchainviewBalanceOf): + def __init__(self, admin, metadata, token_metadata): + FA2.Fa2SingleAsset.__init__(self, metadata=metadata, token_metadata=token_metadata) + FA2.Admin.__init__(self, admin) + + @sp.onchain_view() + def is_admin(self, address): + sp.result(address == self.data.administrator) + + +# @sp.add_test(name="FA2DummyContract") +# def test(): +# alice = sp.test_account("Alice") +# +# c1 = SingleAssetToken(admin=alice.address, metadata=sp.big_map({"ss": sp.bytes("0x0dae11")}), +# token_metadata=sp.map({"ff": sp.bytes("0x0dae11")})) +# +# scenario = sp.test_scenario() +# scenario += c1 + + +sp.add_compilation_target("fa2_dummy", + SingleAssetToken( + admin=sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP"), + metadata=sp.utils.metadata_of_url( + "ipfs://example"), + token_metadata=FA2.make_metadata(name="NativeWrappedCoin", decimals=6, symbol="WTEZ"))) diff --git a/smartpy/bmc/contracts/tests/integration_test.py b/smartpy/bmc/contracts/tests/integration_test.py new file mode 100644 index 000000000..c789986e2 --- /dev/null +++ b/smartpy/bmc/contracts/tests/integration_test.py @@ -0,0 +1,725 @@ +import subprocess +import smartpy as sp + +t_balance_of_request = sp.TRecord(owner=sp.TAddress, token_id=sp.TNat).layout(("owner", "token_id")) +t_balance_of_response = sp.TRecord(request=t_balance_of_request, balance=sp.TNat).layout(("request", "balance")) + +path_bts = {"bts_core": [ + {"types": ['types = sp.io.import_script_from_url("file:../bts/contracts/src/Types.py")', + 'types = sp.io.import_script_from_url("file:./contracts/src/Types.py")']}, + {"FA2_contract": [ + 'FA2_contract = sp.io.import_script_from_url("file:../bts/contracts/src/FA2_contract.py")', + 'FA2_contract = sp.io.import_script_from_url("file:./contracts/src/FA2_contract.py")']} +], + "bts_periphery": [ + {"types": ['types = sp.io.import_script_from_url("file:../bts/contracts/src/Types.py")', + 'types = sp.io.import_script_from_url("file:./contracts/src/Types.py")']}, + {"strings": ['strings = sp.io.import_script_from_url("file:../bts/contracts/src/String.py")', + 'strings = sp.io.import_script_from_url("file:./contracts/src/String.py")']}, + {"rlp": ['rlp = sp.io.import_script_from_url("file:../bts/contracts/src/RLP_struct.py")', + 'rlp = sp.io.import_script_from_url("file:./contracts/src/RLP_struct.py")']}, + + ], + "RLP_struct": [ + {"types": ['types = sp.io.import_script_from_url("file:../bts/contracts/src/Types.py")', + 'types = sp.io.import_script_from_url("file:./contracts/src/Types.py")']}, + ] +} + +path_bmc = { + "RLP_struct": [ + {'_to_byte': + [ + ' _to_byte = sp.view("encode_nat", self.data.helper, sp.as_nat(params.sn), t=sp.TBytes).open_some()', + ' _to_byte = sp.view("to_byte", self.data.helper_parse_negative, params.sn,' + ' t=sp.TBytes).open_some()' + ] + }, + {'_to_int': + [' _to_int = sp.to_int(Utils2.Int.of_bytes(sn_in_bytes))', + ' _to_int = sp.view("to_int", self.data.helper_parse_negative, sn_in_bytes, t=sp.TInt)' + '.open_some()' + ] + } + ], + "bmc_periphery": [ + {'_self_address': + [' _self_address = sp.address("KT1T7kJERDbURCeZGBpTVq6yUR6pkWFJcfRG")', + ' _self_address = sp.self_address' + ] + } + ] +} + +# in cas of new env: change above _self_address to bmc_periphery address of new env + + +def patch_file_path(file_name, old_value, new_value): + subprocess.call("sed -i -e 's#.*" + old_value + " =.*#" + new_value + "#' " + file_name, shell=True) + + +def bts_core_contract_deploy_setup(): + for key, value in path_bts.items(): + for i in value: + lis1 = [] + for x, y in i.items(): + lis1.append(x) + # lis1.append(y) + patch_file_path("../bts/contracts/src/" + key + ".py", lis1[0], y[0]) + + +def bmc_periphery_contract_deploy_setup(): + for key, value in path_bmc.items(): + for i in value: + lis1 = [] + for x, y in i.items(): + lis1.append(x) + # lis1.append(y) + patch_file_path("./contracts/src/" + key + ".py", lis1[0], y[0]) + + +def tear_down_bts_changes(): + for key, value in path_bts.items(): + for i in value: + lis1 = [] + for x, y in i.items(): + lis1.append(x) + # lis1.append(y) + patch_file_path("../bts/contracts/src/" + key + ".py", lis1[0], y[1]) + + +def tear_down_bmc_changes(): + for key, value in path_bmc.items(): + for i in value: + lis1 = [] + for x, y in i.items(): + lis1.append(x) + # lis1.append(y) + patch_file_path("./contracts/src/" + key + ".py", lis1[0], y[1]) + + +# import changes in bts_core for testing +bts_core_contract_deploy_setup() +bmc_periphery_contract_deploy_setup() + +BTSCore = sp.io.import_script_from_url("file:../bts/contracts/src/bts_core.py") +BTSOwnerManager = sp.io.import_script_from_url("file:../bts/contracts/src/bts_owner_manager.py") +BTSPeriphery = sp.io.import_script_from_url("file:../bts/contracts/src/bts_periphery.py") + +FA2 = sp.io.import_script_from_url("https://legacy.smartpy.io/templates/fa2_lib.py") +BMCManagement = sp.io.import_script_from_url("file:./contracts/src/bmc_management.py") +BMCPeriphery = sp.io.import_script_from_url("file:./contracts/src/bmc_periphery.py") +BMCHelper = sp.io.import_script_from_url("file:./contracts/src/helper.py") +ParseAddress = sp.io.import_script_from_url("file:../bts/contracts/src/parse_address.py") +fa2_dummy_file = sp.io.import_script_from_url("file:./contracts/tests/fa2_dummy.py") + +# revert the path changes made in bts_core for testing +tear_down_bts_changes() + + +@sp.add_test("IntegrationTest") +def test(): + sc = sp.test_scenario() + + # test account + owner = sp.test_account("Owner") + alice = sp.address("tz1ep7ffKsQCNdgnkPDCVnVkgbmFZP8mFN1G") + bob = sp.address("tz1euHP1ntD4r3rv8BsE5pXpTRBnUFu69wYP") + jack = sp.address("tz1UUvTndciyJJXuPHBEvWxuM9qgECMV31eA") + sam = sp.address("tz1MrAHP91XLXJXBoB3WL52zQ8VDcnH5PeMp") + relay = sp.test_account("Relay") + helper_parse_neg_contract = sp.test_account("helper_parse_neg_contract") + + # in cas of new env: change icon_bmc_address and its block height of new environment + icon_bmc_address = "cx674dbf2aae08b31ecb8174e755b2f0fa42a81298" + icon_bmc_block_height = 10785336 + + # deploy contracts + + helper_contract = deploy_helper_contract() + sc += helper_contract + + bmc_management = deploy_bmc_management(owner.address, helper_contract.address) + sc += bmc_management + + parse_address = deploy_parse_address() + sc += parse_address + + bmc_periphery = deploy_bmc_periphery(bmc_management.address, helper_contract.address, + helper_parse_neg_contract.address, parse_address.address) + sc += bmc_periphery + + bts_owner_manager = deploy_bts_owner_manager_contract(owner.address) + sc += bts_owner_manager + + bts_core = deploy_bts_core(bts_owner_manager.address) + sc += bts_core + + bts_periphery = deploy_bts_periphery(bts_core.address, helper_contract.address, + parse_address.address, bmc_periphery.address, + owner.address) + sc += bts_periphery + + fa2_dummy = fa2_dummy_file.SingleAssetToken(admin=owner.address, + metadata=sp.utils.metadata_of_url( + "ipfs://example"), + token_metadata=FA2.make_metadata(name="NativeWrappedCoin", decimals=6, + symbol="wTEZ")) + sc += fa2_dummy + + fa2_dummy_second = fa2_dummy_file.SingleAssetToken(admin=owner.address, + metadata=sp.utils.metadata_of_url( + "ipfs://example"), + token_metadata=FA2.make_metadata(name="Dummy", + decimals=6, + symbol="PEPE")) + sc += fa2_dummy_second + + # BMC_MANAGEMENT SETTERS + + # set pause status to false + bmc_management.toggle_bridge_on().run(sender=owner.address) + + # set bmc periphery + bmc_management.set_bmc_periphery(bmc_periphery.address).run( + sender=owner.address) + + # set bmc_btp_address + bmc_management.set_bmc_btp_address("NetXnHfVqm9iesp.tezos").run( + sender=owner.address) + + # tear down changes after bmc btp address is set + tear_down_bmc_changes() + + # add_service + svc1 = sp.string("bts") + bmc_management.add_service(sp.record(addr=bts_periphery.address, svc=svc1)).run( + sender=owner.address) + + # add_route + dst = "btp://0x7.icon/" + icon_bmc_address + next_link = "btp://0x7.icon/" + icon_bmc_address + bmc_management.add_route(sp.record(dst=dst, link=next_link)).run( + sender=owner.address) + + # add_link + bmc_management.add_link("btp://0x7.icon/" + icon_bmc_address).run( + sender=owner.address) + + # set_link_rx_height + bmc_management.set_link_rx_height(sp.record(height=icon_bmc_block_height, + link="btp://0x7.icon/" + icon_bmc_address)).run( + sender=owner.address) + + # add_relay + bmc_management.add_relay(sp.record(link="btp://0x7.icon/" + icon_bmc_address, + addr=sp.set([relay.address]))).run( + sender=owner.address) + + # BTS_CORE SETTERS + + # set pause status to false + bts_core.toggle_bridge_on().run(sender=owner.address) + + # update_bts_periphery + bts_core.update_bts_periphery(bts_periphery.address).run(sender=owner.address) + + # set_fee_ratio + bts_core.set_fee_ratio(name=sp.string("btp-NetXnHfVqm9iesp.tezos-XTZ"), fee_numerator=sp.nat(100), + fixed_fee=sp.nat(450)).run(sender=owner.address) + + prev = "btp://0x7.icon/" + icon_bmc_address + + # Tests + # Scenario 1: Init message from relay + msg_byte = sp.bytes( + "0xf8e5f8e3b8e1f8df01b8d7f8d5f8d3b8406274703a2f2f4e6574586e486656716" + "d39696573702e74657a6f732f4b543154376b4a45524462555243655a47427054567136" + "79555236706b57464a6366524701b88ef88cb8396274703a2f2f3078372e69636f6e2f6378363734646" + "26632616165303862333165636238313734653735356232663066613432613831323938b8406274703a2f2f4e6" + "574586e486656716d39696573702e74657a6f732f4b543154376b4a45524462555243655a47427054567136795552" + "36706b57464a6366524783626d630089c884496e697482c1c08400a4927b") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + + # Scenario 2: Add to blacklist called from icon + # add bob and tz1bPkYCh5rTTGL7DuPLB66J8zqnUD8cMRq1 + msg_byte = sp.bytes( + "0xf9014df9014ab90147f9014401b9013bf90138f90135b8406274703a2f2f4e6574586e" + "486656716d39696573702e74657a6f732f4b543154376b4a45524462555243655a47427054" + "56713679555236706b57464a6366524702b8f0f8eeb8396274703a2f2f3078372e69636f6e2f63" + "7836373464626632616165303862333165636238313734653735356232663066613432613831323938" + "b8406274703a2f2f4e6574586e486656716d39696573702e74657a6f732f4b543154376b4a45524462" + "555243655a4742705456713679555236706b57464a636652478362747301b86af86803b865f86300f84" + "aa4747a3165754850316e7444347233727638427345357058705452426e5546753639775950a4747a3162" + "506b59436835725454474c3" + "74475504c4236364a387a716e554438634d527131954e6574586e486656716d39696573702e74657a6f738400a4934b") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify blacklisted address + sc.verify_equal(bts_periphery.data.blacklist, {sp.address("tz1euHP1ntD4r3rv8BsE5pXpTRBnUFu69wYP"): True, + sp.address("tz1bPkYCh5rTTGL7DuPLB66J8zqnUD8cMRq1"): True}) + + # Scenario 3: Remove from blacklist called from icon + # remove bob + msg_byte = sp.bytes( + "0xf90127f90124b90121f9011e01b90115f90112f9010fb8406274703a2f2f4e6574586e486656716d39696573702e74657a6f732" + "f4b543154376b4a45524462555243655a4742705456713679555236706b57464a6366524703b8caf8c8b8396274703" + "a2f2f3078372e69636f6e2f637836373464626632616165303862333165636238313734653735356232663066613432613831323" + "938b8406274703a2f2f4e6574586e486656716d39696573702e74657a6f732f4b543154376b4a45524462555243655a47427054" + "56713679555236706b57464a636652478362747302b844f84203b83ff83d01e5a4747a3165754850316e744434723372763842734" + "5357058705452426e5546753639775950954e6574586e486656716d39696573702e74657a6f738400a49366") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify one blacklisted address is removed + sc.verify(bts_periphery.data.blacklist.get(sp.address("tz1bPkYCh5rTTGL7DuPLB66J8zqnUD8cMRq1")) == True) + + # Scenario 4: Transfer native coin from icon to tezos + # transferred: ICX: 25*10**18 + # fee deducted on icon: 4550000000000000000 + # receiver address: bob + + # register icon native coin + bts_core.register( + name=sp.string("btp-0x7.icon-ICX"), + fee_numerator=sp.nat(100), + fixed_fee=sp.nat(4300000000000000000), + addr=sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg"), + token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")}) + ).run(sender=owner.address) + + msg_byte = sp.bytes( + "0xf90157f90154b90151f9014e01b90145f90142f9013fb8406274703a2f2f4e6574586e486656716d39" + "696573702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57" + "464a6366524704b8faf8f8b8396274703a2f2f3078372e69636f6e2f63783637346462663261616530" + "3862333165636238313734653735356232663066613432613831323938b8406274703a2f2f4e6574586e" + "486656716d39696573702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555" + "236706b57464a636652478362747303b874f87200b86ff86daa6878396431383164313366343763356161" + "65353535623730393831346336623232393738373937363139a4747a3165754850316e7444347233727638" + "427345357058705" + "452426e5546753639775950dcdb906274702d3078372e69636f6e2d49435889011bccfea6b8bd00008400a493c0") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify native coin balance + coin_address = bts_core.data.coins.get("btp-0x7.icon-ICX") + user_balance = sp.view("get_balance_of", coin_address, + [sp.record(owner=bob, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + + sc.verify_equal(user_balance, [sp.record(request=sp.record(owner=bob, + token_id=sp.nat(0)), + balance=sp.nat(20450000000000000000))]) + + # Scenario 5: Transfer ICON IRC2 coin bnUSD4 from icon to tezos without registering on tezos + # transferred: bnUSD4: 50*10**18 + # receiver address: jack + msg_byte = sp.bytes( + "0xf9015af90157b90154f9015101b90148f90145f90142b8406274703a2f2f4e6574586e48665" + "6716d39696573702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555" + "236706b57464a6366524705b8fdf8fbb8396274703a2f2f3078372e69636f6e2f6378363734646266" + "32616165303862333165636238313734653735356232663066613432613831323938b8406274703a2" + "f2f4e6574586e486656716d39696573702e74657a6f732f4b543154376b4a45524462555243655a47" + "42705456713679555236706b57464a636652478362747304b877f87500b872f870aa687839643138316" + "431336634376335616165353535623730393831346336623232393738373937363139a4747a314d724" + "148503931584c584a58426f4233574c35327a51385644636e48355" + "0654d70dfde936274702d3078372e69636f6e2d626e555344348902b5e3af16b18800008400a494e9") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # no changes happen on tezos so no need to assert + + # Scenario 6: Transfer ICON IRC2 coin bnUSD4 from icon to tezos + # transferred: bnUSD4: 50*10**18 + # receiver address: jack + # register icon coin bnUSD4 + bts_core.register( + name=sp.string("btp-0x7.icon-bnUSD4"), + fee_numerator=sp.nat(0), + fixed_fee=sp.nat(0), + addr=sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg"), + token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")}) + ).run(sender=owner.address) + + msg_byte = sp.bytes( + "0xf9015af90157b90154f9015101b90148f90145f90142b8406274703a2f2f4e6574586e48665671" + "6d39696573702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b" + "57464a63665247" + "06b8fdf8fbb8396274703a2f2f3078372e69636f6e2f637836373464626632616165303862333165636238313" + "734653735356232663066613432613831323938b8406274703a2f2f4e6574586e486656716d39696573702e74657a6" + "f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a636652478362747305b877f" + "87500b872f870aa68783964313831643133663437633561616535353562373039383134633662323239373837393736" + "3139a4747a31555576546e646369794a4a587550484245765778754d39716745434d5633316541dfde936274702d3" + "078372e69636f6e2d626e555344348902b5e3af16b18800008400a49561") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify bnUSD4 coin balance + coin_address = bts_core.data.coins.get("btp-0x7.icon-bnUSD4") + user_balance = sp.view("get_balance_of", coin_address, + [sp.record(owner=jack, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + + sc.verify_equal(user_balance, [sp.record(request=sp.record(owner=jack, + token_id=sp.nat(0)), + balance=sp.nat(50000000000000000000))]) + + # Scenario 7: Transfer batch from icon to tezos + # transferred: icon native coin: 20*10**18 and bnUSD4:14*10**18 + # receiver address: alice + msg_byte = sp.bytes( + "0xf90179f90176b90173f9017001b90167f90164f90161b8406274703a2f2f4e6574586e486656716" + "d39696573702e74657a6f732f4b543154376b4a45524462555243655a474270545671367955523670" + "6b57464a6366524707b9011bf90118b8396274703a2f2f3078372e69636f6e2f63783637346462663" + "2616165303862333165636238313734653735356232663066613432613831323938b8406274703a2f2" + "f4e6574586e486656716d39696573702e74657a6f732f4b543154376b4a45524462555243655a47427" + "05456713679555236706b57464a636652478362747306b894f89200b88ff88daa68783964313831643" + "1336634376335616165353535623730393831346336623232393738373937363139a4747a316570376" + "6664b7351434e64676e6b504443566e566b67626d465a50386d464e3147f83bdb906274702d3078372" + "e69636f6e2d4943588900d71b0fe0" + "a28e0000de936274702d3078372e69636f6e2d626e555344348900c249fdd3277800008400a4959d") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify native coin balance + coin_address = bts_core.data.coins.get("btp-0x7.icon-ICX") + user_balance = sp.view("get_balance_of", coin_address, + [sp.record(owner=alice, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + + sc.verify_equal(user_balance, [sp.record(request=sp.record(owner=alice, + token_id=sp.nat(0)), + balance=sp.nat(15500000000000000000))]) + + # verify bnUSD4 coin balance + coin_address = bts_core.data.coins.get("btp-0x7.icon-bnUSD4") + user_balance = sp.view("get_balance_of", coin_address, + [sp.record(owner=alice, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + + sc.verify_equal(user_balance, [sp.record(request=sp.record(owner=alice, + token_id=sp.nat(0)), + balance=sp.nat(14000000000000000000))]) + + # Scenario 8: Transfer batch from icon to tezos + # transferred 20 *10**18 bnUSD4 + # receiver address: alice + msg_byte = sp.bytes( + "0xf9015af90157b90154f9015101b90148f90145f90142b8406274703a2f2f4e6574586e486656716d3969657" + "3702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a636652" + "4708b8fdf8fbb8396274703a2f2f3078372e69636f6e2f63783637346462663261616530386233316563623" + "8313734653735356232663066613432613831323938b8406274703a2f2f4e6574586e486656716d39696573" + "702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a6366524" + "78362747307b877f87500b872f870aa68783964313831643133663437633561616535353562373039383134" + "6336623232393738373937363139a4747a3165703766664b7351434e64676e6b504443566e566b67626d465" + "a50386d464e3147dfde936274702d3078372e69636f6e2d626e555344348901158e460913d000008400a495f3") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + + # verify bnUSD4 coin balance + # alice had 14*10**18 bnUSD4 initially + coin_address = bts_core.data.coins.get("btp-0x7.icon-bnUSD4") + user_balance = sp.view("get_balance_of", coin_address, + [sp.record(owner=alice, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + + sc.verify_equal(user_balance, [sp.record(request=sp.record(owner=alice, + token_id=sp.nat(0)), + balance=sp.nat(14000000000000000000 + 20000000000000000000))]) + + # Scenario 9: Set token limit of bnUSD4 from icon + # token limit of bnUSD4: 21 * 10**18 + msg_byte = sp.bytes( + "0xf9011ef9011bb90118f9011501b9010cf90109f90106b8406274703a2f2f4e6574586e486656716d396965" + "73702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a6366" + "524709b8c1f8bfb8396274703a2f2f3078372e69636f6e2f63783637346462663261616530386233316563" + "6238313734653735356232663066613432613831323938b8406274703a2f2f4e6574586e486656716d3969" + "6573702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a63" + "6652478362747308b83bf83904b7f6d4936274702d3078372e69636f6e2d626e55534434ca8901236efcbc" + "bb340000954e6574586e486656716d39696573702e74657a6f738400a49624") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify token limit + sc.verify(bts_periphery.data.token_limit.get("btp-0x7.icon-bnUSD4") == sp.nat(21000000000000000000)) + + # Scenario 10: Set token limit of btp-0x7.icon-bnUSD4 and btp-0x7.icon-ICX from icon + # token limit of btp-0x7.icon-ICX: 43*10**18 + # token limit of btp-0x7.icon-bnUSD4: 32*10**18 + msg_byte = sp.bytes( + "0xf9013bf90138b90135f9013201b90129f90126f90123b8406274703a2f2f4e6574586e486656716d39696573702e" + "74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a636652470ab8def8" + "dcb8396274703a2f2f3078372e69636f6e2f63783637346462663261616530386233316563623831373465373535" + "6232663066613432613831323938b8406274703a2f2f4e6574586e486656716d39696573702e74657a6f732f4b543" + "154376b4a45524462555243655a4742705456713679555236706b57464a636652478362747309b858f85604b853f8" + "51e5936274702d3078372e69636f6e2d626e55534434906274702d3078372e69636f6e2d494358d48901bc16d674e" + "c800000890254beb02d1dcc0000954e6574586e486656716d39696573702e74657a6f738400a4965c") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify token limits + sc.verify(bts_periphery.data.token_limit.get("btp-0x7.icon-ICX") == sp.nat(43000000000000000000)) + sc.verify(bts_periphery.data.token_limit.get("btp-0x7.icon-bnUSD4") == sp.nat(32000000000000000000)) + + # Scenario 11: Transfer btp-0x7.icon-bnUSD4 from icon to tezos + # transferred btp-0x7.icon-bnUSD4: 32*10**18 + # receiver address: sam + msg_byte = sp.bytes( + "0xf9015af90157b90154f9015101b90148f90145f90142b8406274703a2f2f4e6574586e486656716d396965" + "73702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a6366" + "52470bb8fdf8fbb8396274703a2f2f3078372e69636f6e2f63783637346462663261616530386233316563" + "6238313734653735356232663066613432613831323938b8406274703a2f2f4e6574586e486656716d3969" + "6573702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a636" + "65247836274730ab877f87500b872f870aa6878396431383164313366343763356161653535356237303938" + "31346336623232393738373937363139a4747a314d724148503931584c584a58426f4233574c35327a51385" + "644636e483550654d70dfde936274702d3078372e69636f6e2d626e555344348901bc16d674ec8000008400a496c8") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify bnUSD4 coin balance + coin_address = bts_core.data.coins.get("btp-0x7.icon-bnUSD4") + user_balance = sp.view("get_balance_of", coin_address, + [sp.record(owner=sam, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + + sc.verify_equal(user_balance, [sp.record(request=sp.record(owner=sam, + token_id=sp.nat(0)), + balance=sp.nat(32000000000000000000))]) + + # Tezos to icon scenarios + + # Scenario 12: Transfer native coin from tezos to icon + # transferred btp-NetXnHfVqm9iesp.tezos-XTZ: 9000000 + # fee deducted on tezos: 90450 + + bts_core.transfer_native_coin("btp://0x7.icon/hx9d181d13f47c5aae555b709814c6b22978797619").run( + sender=alice, amount=sp.mutez(9000000)) + + # relay msg for transfer end + msg_byte = sp.bytes( + "0xf8f2f8f0b8eef8ec01b8e4f8e2f8e0b8406274703a2f2f4e6574586e486656716d39696573702e74657" + "a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a636652470cb89" + "bf899b8396274703a2f2f3078372e69636f6e2f637836373464626632616165303862333165636238313" + "734653735356232663066613432613831323938b8406274703a2f2f4e6574586e486656716d3969657370" + "2e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a63665247" + "836274730196d50293d200905472616e7366657220537563636573738400a498d2") + + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify aggregation fee + # sc.verify(bts_core.data.aggregation_fee.get("btp-NetXnHfVqm9iesp.tezos-XTZ") == sp.nat(90450)) + + # Scenario 13: Transfer wrapped token of btp-NetXnHfVqm9iesp.tezos-XTZ from icon to tezos + # receiver address: tz1MrAHP91XLXJXBoB3WL52zQ8VDcnH5PeMp + # received amount: 3*10**6 + + msg_byte = sp.bytes("0xf9015ff9015cb90159f9015601b9014df9014af90147b8406274703a2f2f4e6" + "574586e486656716d39696573702e74657a6f732f4b543154376b4a45524462555" + "243655a4742705456713679555236706b57464a636652470db90101f8ffb839627" + "4703a2f2f3078372e69636f6e2f6378363734646266326161653038623331656362" + "38313734653735356232663066613432613831323938b8406274703a2f2f4e657458" + "6e486656716d39696573702e74657a6f732f4b543154376b4a45524462555243655a" + "4742705456713679555236706b57464a63665247836274730bb87bf87900b876f874" + "aa6878396431383164313366343763356161653535356237303938313463366232323" + "93738373937363139a4747a314d724148503931584c584a58426f4233574c35327a51" + "385644636e483550654d70e3e29d6274702d4e6574586e486656716d39696573702e7" + "4657a6f732d58545a832dc6c08400a49939") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + + # Scenario 14: Transfer fa2 token from tezos to icon + # mint fa2 dummy + fa2_dummy.mint([sp.record(to_=alice, amount=sp.nat(1000000000))]).run(sender=owner.address) + + # add operator + fa2_dummy.update_operators( + [sp.variant("add_operator", sp.record(owner=alice, operator=bts_core.address, token_id=0))]).run( + sender=alice) + + # register fa2 + bts_core.register( + name=sp.string("btp-0x7.tezos-fa2"), + fee_numerator=sp.nat(0), + fixed_fee=sp.nat(0), + addr=fa2_dummy.address, + token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")}) + ).run(sender=owner.address) + + # transfer fa2 token + bts_core.transfer(sp.record( + coin_name="btp-0x7.tezos-fa2", value=10000000, + to="btp://0x7.icon/hx9d181d13f47c5aae555b709814c6b22978797619")).run(sender=alice) + + # relay msg for end of transfer fa2 + msg_byte = sp.bytes( + "0xf8f2f8f0b8eef8ec01b8e4f8e2f8e0b8406274703a2f2f4e6574586e486656716d3969657370" + "2e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b57464a6" + "36652470eb89bf899b8396274703a2f2f3078372e69636f6e2f63783637346462663261616530386" + "2333165636238313734653735356232663066613432613831323938b8406274703a2f2f4e6574586" + "e486656716d39696573702e74657a6f732f4b543154376b4a45524462555243655a4742705456713" + "679555236706b57464a63665247836274730296d50293d200905472616e7366657220537563636573738400a49c03") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify aggregation fee + sc.verify(bts_core.data.aggregation_fee.get("btp-0x7.tezos-fa2") == sp.nat(0)) + + # Scenario 15: Transfer batch fa2 token and native token from tezos to icon + bts_core.transfer_batch(sp.record(coin_names_values={"btp-0x7.tezos-fa2": 20000000}, + to="btp://0x7.icon/hx9d181d13f47c5aae555b709814c6b22978797619")).run( + sender=alice, amount=sp.mutez(60000000)) + # relay msg for end of transfer batch + msg_byte = sp.bytes( + "0xf8f2f8f0b8eef8ec01b8e4f8e2f8e0b8406274703a2f2f4e6574586e486656716d3969657" + "3702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236706b5" + "7464a636652470fb89bf899b8396274703a2f2f3078372e69636f6e2f637836373464626632" + "616165303862333165636238313734653735356232663066613432613831323938b8406274703" + "a2f2f4e6574586e486656716d39696573702e74657a6f732f4b543154376b4a455244625552436" + "55a4742705456713679555236706b57464a63665247836274730396d50293d200905472616e736" + "6657220537563636573738400a49e6e") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify aggregation fee + # existing fee of btp-NetXnHfVqm9iesp.tezos-XTZ: 90450 + sc.verify(bts_core.data.aggregation_fee.get("btp-0x7.tezos-fa2") == sp.nat(0)) + sc.verify(bts_core.data.aggregation_fee.get("btp-NetXnHfVqm9iesp.tezos-XTZ") == sp.nat(600450 + 90450)) + + # Scenario 16: Transfer native coin from icon to tezos + # receiving address: tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP + msg_byte = sp.bytes( + "0xf90157f90154b90151f9014e01b90145f90142f9013fb8406274703a2f2f4e6574586e486" + "656716d39696573702e74657a6f732f4b543154376b4a45524462555243655a4742705456713" + "679555236706b57464a6366524710b8faf8f8b8396274703a2f2f3078372e69636f6e2f63783" + "6373464626632616165303862333165636238313734653735356232663066613432613831323" + "938b8406274703a2f2f4e6574586e486656716d39696573702e74657a6f732f4b543154376b4a" + "45524462555243655a4742705456713679555236706b57464a63665247836274730cb874f872" + "00b86ff86daa68783964313831643133663437633561616535353562373039383134633662323" + "2393738373937363139a4747a316733704a5a50696678684e3439756b435a6a644551747957675" + "832455264667150dcdb906274702d3078372e69636f6e2d49435889011bccfea6b8bd00008400a49e9d") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + + # Scenario 17: Transfer batch native coin and one fa2 tokens from tezos to icon + bts_core.transfer_batch(sp.record(coin_names_values={"btp-0x7.tezos-fa2": 10000000}, + to="btp://0x7.icon/hx9d181d13f47c5aae555b709814c6b22978797619")).run( + sender=alice, amount=sp.mutez(30000000)) + # relay msg for end of transfer batch + msg_byte = sp.bytes( + "0xf8f2f8f0b8eef8ec01b8e4f8e2f8e0b8406274703a2f2f4e6574586e486656716d3969" + "6573702e74657a6f732f4b543154376b4a45524462555243655a4742705456713679555236" + "706b57464a6366524711b89bf899b8396274703a2f2f3078372e69636f6e2f63783637346462" + "6632616165303862333165636238313734653735356232663066613432613831323938b840627" + "4703a2f2f4e6574586e486656716d39696573702e74657a6f732f4b543154376b4a45524462555" + "243655a4742705456713679555236706b57464a63665247836274730496d50293d200905472616" + "e7366657220537563636573738400a49f1a") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # verify aggregation fee + # existing fee of btp-NetXnHfVqm9iesp.tezos-XTZ: 690900 + sc.verify(bts_core.data.aggregation_fee.get("btp-0x7.tezos-fa2") == sp.nat(0)) + sc.verify(bts_core.data.aggregation_fee.get("btp-NetXnHfVqm9iesp.tezos-XTZ") == sp.nat(690900 + 300450)) + + # Scenario 18: Transfer fa2 token not registered on icon from tezos + # mint fa2 dummy second + fa2_dummy_second.mint([sp.record(to_=bob, amount=sp.nat(1000000000))]).run(sender=owner.address) + + # add operator + fa2_dummy_second.update_operators( + [sp.variant("add_operator", sp.record(owner=bob, operator=bts_core.address, token_id=0))]).run( + sender=bob) + + # register fa2 + bts_core.register( + name=sp.string("btp-0x7.tezos-fa2-second"), + fee_numerator=sp.nat(0), + fixed_fee=sp.nat(0), + addr=fa2_dummy_second.address, + token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")}) + ).run(sender=owner.address) + + # transfer fa2 token + bts_core.transfer(sp.record( + coin_name="btp-0x7.tezos-fa2-second", value=10000000, + to="btp://0x7.icon/hx9d181d13f47c5aae555b709814c6b22978797619")).run(sender=bob) + + user_balance_before = sp.view("get_balance_of", fa2_dummy_second.address, + [sp.record(owner=bob, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + # relay msg for end of transfer fa2 + msg_byte = sp.bytes( + "0xf8e1f8dfb8ddf8db01b8d3f8d1f8cfb8406274703a2f2f4e6574586e486656" + "716d39696573702e74657a6f732f4b543154376b4a45524462555243655a47427" + "05456713679555236706b57464a6366524712b88af888b8396274703a2f2f3078" + "372e69636f6e2f6378363734646266326161653038623331656362383137346537" + "35356232663066613432613831323938b8406274703a2f2f4e6574586e48665671" + "6d39696573702e74657a6f732f4b543154376b4a45524462555243655a47427054" + "56713679555236706b57464a636652478362747381fb84c328f8008400a4b09d") + bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + user_balance_after = sp.view("get_balance_of", fa2_dummy_second.address, + [sp.record(owner=bob, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + + sc.verify_equal(user_balance_before, user_balance_after) + + # sc.verify_equal(user_balance, [sp.record(request=sp.record(owner=sam, + # token_id=sp.nat(0)), + # balance=sp.nat(32000000000000000000))]) + + # these case cannot be tested in integration test due to limitation on tezos + # # Scenario 12: Transferred btp-0x7.icon-ICX wrapped coin from tezos to icon + # coin_address = bts_core.data.coins.get("btp-0x7.icon-ICX") + # # contract = sp.contract(sp.TRecord(spender=sp.TAddress, amount=sp.TNat), coin_address, "set_allowance").open_some() + # + # # set allowance for bts_core + # coin_address.set_allowance([sp.record(spender=bts_core.address, + # amount=sp.nat(1000000000000000000000))]) + # sc.verify(sp.view("get_allowance", coin_address, sp.record(spender=bts_core.address, owner=alice), + # t=sp.TNat).open_some("Invalid view") == sp.nat(0)) + # # update operator + # coin_address.update_operators( + # [sp.variant("add_operator", sp.record(owner=alice, operator=bts_core.address, token_id=0))]) + # + # bts_balance_before = sp.view("get_balance_of", coin_address, + # [sp.record(owner=bts_core.address, token_id=sp.nat(0))], + # t=sp.TList(t_balance_of_response)).open_some("Invalid view") + # + # # transfer wrapped coin + # bts_core.transfer(sp.record(coin_name="btp-0x7.icon-ICX", value=sp.nat(13000000000000000000), + # to=" btp://0x7.icon/hx9d181d13f47c5aae555b709814c6b22978797619")).run(sender=alice) + # # transfer end message from relay + # msg_byte = sp.bytes( + # "0xf8f2f8f0b8eef8ec01b8e4f8e2f8e0b8406274703a2f2f4e6574586e486656716d39696573702e74657a6f732f4b5431574b42484c" + # "4662674c38514a574779536635434b32684b3369434c454d395a69540cb89bf899b8396274703a2f2f3078372e69636f6e2f63783531" + # "6530626238353833396530653366666662346330313430616530663038336538393834363464b8406274703a2f2f4e6574586e486656" + # "716d39696573702e74657a6f732f4b5431574b42484c4662674c38514a574779536635434b32684b3369434c454d395a695483627473" + # "0196d50293d200905472616e7366657220537563636573738400a2b2de") + # bmc_periphery.handle_relay_message(sp.record(prev=prev, msg=msg_byte)).run(sender=relay.address) + # + # bts_balance_after = sp.view("get_balance_of", coin_address, + # [sp.record(owner=bts_core.address, token_id=sp.nat(0))], + # t=sp.TList(t_balance_of_response)).open_some("Invalid view") + # verify burnt amount + + # Scenario 13: Transferred btp-0x7.icon-ICX wrapped coin from tezos to icon + + +def deploy_bmc_management(owner, helper): + bmc_management = BMCManagement.BMCManagement(owner, helper) + return bmc_management + + +def deploy_bmc_periphery(bmc_address, helper, helper_parse_neg_contract, parse): + bmc_periphery = BMCPeriphery.BMCPreiphery(bmc_address, helper, helper_parse_neg_contract, parse) + return bmc_periphery + + +def deploy_helper_contract(): + helper_contract = BMCHelper.Helper() + return helper_contract + + +def deploy_parse_address(): + parse_address = ParseAddress.ParseAddress() + return parse_address + + +def deploy_bts_core(bts_owner_manager_contract): + bts_core = BTSCore.BTSCore( + owner_manager=bts_owner_manager_contract, + _native_coin_name="btp-NetXnHfVqm9iesp.tezos-XTZ", + _fee_numerator=sp.nat(100), + _fixed_fee=sp.nat(450) + ) + return bts_core + + +def deploy_bts_owner_manager_contract(owner): + bts_owner_manager_contract = BTSOwnerManager.BTSOwnerManager(owner) + return bts_owner_manager_contract + + +def deploy_bts_periphery(core_address, helper, parse, bmc, owner): + bts_periphery_contract = BTSPeriphery.BTSPeriphery(bmc_address=bmc, bts_core_address=core_address, + helper_contract=helper, parse_address=parse, + owner_address=owner, + native_coin_name="btp-NetXnHfVqm9iesp.tezos-XTZ") + return bts_periphery_contract diff --git a/smartpy/bmc/package-lock.json b/smartpy/bmc/package-lock.json new file mode 100644 index 000000000..2bec67e40 --- /dev/null +++ b/smartpy/bmc/package-lock.json @@ -0,0 +1,1431 @@ +{ + "name": "create-tezos-app", + "version": "1.0.3", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "create-tezos-app", + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "@taquito/signer": "^14.0.0", + "@taquito/taquito": "^14.0.0", + "dotenv": "^16.0.3", + "ts-node": "^10.9.1" + }, + "bin": { + "create-tezos-app": "bin/cli.js" + }, + "devDependencies": { + "typescript": "^4.8.4" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "dependencies": { + "@stablelib/int": "^1.0.1" + } + }, + "node_modules/@stablelib/blake2b": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/blake2b/-/blake2b-1.0.1.tgz", + "integrity": "sha512-B3KyKoBAjkIFeH7romcF96i+pVFYk7K2SBQ1pZvaxV+epSBXJ+n0C66esUhyz6FF+5FbdQVm77C5fzGFcEZpKA==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/bytes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==" + }, + "node_modules/@stablelib/constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz", + "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==" + }, + "node_modules/@stablelib/ed25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", + "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "dependencies": { + "@stablelib/random": "^1.0.2", + "@stablelib/sha512": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==" + }, + "node_modules/@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==" + }, + "node_modules/@stablelib/keyagreement": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", + "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "dependencies": { + "@stablelib/bytes": "^1.0.1" + } + }, + "node_modules/@stablelib/nacl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@stablelib/nacl/-/nacl-1.0.4.tgz", + "integrity": "sha512-PJ2U/MrkXSKUM8C4qFs87WeCNxri7KQwR8Cdwm9q2sweGuAtTvOJGuW0F3N+zn+ySLPJA98SYWSSpogMJ1gCmw==", + "dependencies": { + "@stablelib/poly1305": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1", + "@stablelib/x25519": "^1.0.3", + "@stablelib/xsalsa20": "^1.0.2" + } + }, + "node_modules/@stablelib/poly1305": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz", + "integrity": "sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==", + "dependencies": { + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/salsa20": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/salsa20/-/salsa20-1.0.2.tgz", + "integrity": "sha512-nfjKzw0KTKrrKBasEP+j7UP4I8Xudom8lVZIBCp0kQNARXq72IlSic0oabg2FC1NU68L4RdHrNJDd8bFwrphYA==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/sha512": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", + "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==" + }, + "node_modules/@stablelib/x25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", + "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "dependencies": { + "@stablelib/keyagreement": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/xsalsa20": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/xsalsa20/-/xsalsa20-1.0.2.tgz", + "integrity": "sha512-7XdBGbcNgBShmuhDXv1G1WPVCkjZdkb1oPMzSidO7Fve0MHntH6TjFkj5bfLI+aRE+61weO076vYpP/jmaAYog==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/salsa20": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@taquito/http-utils": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-14.0.0.tgz", + "integrity": "sha512-ZWZzod/+/OEE26b9CnDRjHGfUKBJft3aXv/e/A9bTHAtvRNJqGIhofHcDg/jTaolBMarCF2b3XBYw35aOOSk4A==", + "dependencies": { + "axios": "^0.26.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/local-forging": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-14.0.0.tgz", + "integrity": "sha512-Nm0xGmS1Jzd+tU0a/8Y8XuTghbiPBgHDLo+e4141TK3OAwTzOw0an+w3xK9QVfzvxfIcZBSMjeMZzOwDdiqkJQ==", + "dependencies": { + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/michel-codec": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-14.0.0.tgz", + "integrity": "sha512-ftnBvUVddlHBqvQbGPHEb26KrS4lIcaZ1eIpYJWiz+akb4Pcfyq7j/OEsDZbB7Pl2FP9hqu7ZygOF34zY6Lrtw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/michelson-encoder": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-14.0.0.tgz", + "integrity": "sha512-KIS+xl4rKfnd6hf9LUr6W+Pb7gv8F/Qsx0fho9CtM2PodKvdef3YlvkpScBUM9QZntAlvq2XQXUVXcZkbvxygw==", + "dependencies": { + "@taquito/rpc": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2", + "fast-json-stable-stringify": "^2.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/rpc": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-14.0.0.tgz", + "integrity": "sha512-FMfb80sA+VJwNx8OTNN07boDAt2roISqLLCUgmOIwy/cFDqhII7gdS4aYWJWqlKbdPKCPqh3a3ZQD1X/jyQHOA==", + "dependencies": { + "@taquito/http-utils": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/signer": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-14.0.0.tgz", + "integrity": "sha512-vDqp/quzAsOiVikUt5MYUKhHI3S9qlasazyXs99xK9qpGLotbx6aseKcfb/dkaTo4/eoMzP4XzTVdnk0AqcCkw==", + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.2", + "@stablelib/nacl": "^1.0.3", + "@taquito/taquito": "^14.0.0", + "@taquito/utils": "^14.0.0", + "elliptic": "^6.5.4", + "pbkdf2": "^3.1.2", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/taquito": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-14.0.0.tgz", + "integrity": "sha512-JaXfvqOCF3dkwXxhe00Ravb8WLMC2VqJxerf4GrGUEpJw+NAkwbGEb8k/52+MQQdlxMPepZdPPru/HM3nFG0Tw==", + "hasInstallScript": true, + "dependencies": { + "@taquito/http-utils": "^14.0.0", + "@taquito/local-forging": "^14.0.0", + "@taquito/michel-codec": "^14.0.0", + "@taquito/michelson-encoder": "^14.0.0", + "@taquito/rpc": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2", + "rxjs": "^6.6.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/utils": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-14.0.0.tgz", + "integrity": "sha512-0RSHn/CzbcbMdldJJIlXyxOvAajwmL6iPKJ6NaRyYJqqLM2CxYjG72KpXVv716pCMV1MbIWsOAr9FKbxW73PsA==", + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.2", + "@types/bs58check": "^2.1.0", + "bignumber.js": "^9.0.2", + "blakejs": "^1.2.1", + "bs58check": "^2.1.2", + "buffer": "^6.0.3", + "elliptic": "^6.5.4", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, + "node_modules/@types/bs58check": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.0.tgz", + "integrity": "sha512-OxsysnJQh82vy9DRbOcw9m2j/WiyqZLn0YBhKxdQ+aCwoHj+tWzyCgpwAkr79IfDXZKxc6h7k89T9pwS78CqTQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + }, + "node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz", + "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==", + "engines": { + "node": "*" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/typedarray-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz", + "integrity": "sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "requires": { + "@stablelib/int": "^1.0.1" + } + }, + "@stablelib/blake2b": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/blake2b/-/blake2b-1.0.1.tgz", + "integrity": "sha512-B3KyKoBAjkIFeH7romcF96i+pVFYk7K2SBQ1pZvaxV+epSBXJ+n0C66esUhyz6FF+5FbdQVm77C5fzGFcEZpKA==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/bytes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==" + }, + "@stablelib/constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz", + "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==" + }, + "@stablelib/ed25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", + "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "requires": { + "@stablelib/random": "^1.0.2", + "@stablelib/sha512": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==" + }, + "@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==" + }, + "@stablelib/keyagreement": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", + "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "requires": { + "@stablelib/bytes": "^1.0.1" + } + }, + "@stablelib/nacl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@stablelib/nacl/-/nacl-1.0.4.tgz", + "integrity": "sha512-PJ2U/MrkXSKUM8C4qFs87WeCNxri7KQwR8Cdwm9q2sweGuAtTvOJGuW0F3N+zn+ySLPJA98SYWSSpogMJ1gCmw==", + "requires": { + "@stablelib/poly1305": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1", + "@stablelib/x25519": "^1.0.3", + "@stablelib/xsalsa20": "^1.0.2" + } + }, + "@stablelib/poly1305": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz", + "integrity": "sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==", + "requires": { + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/salsa20": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/salsa20/-/salsa20-1.0.2.tgz", + "integrity": "sha512-nfjKzw0KTKrrKBasEP+j7UP4I8Xudom8lVZIBCp0kQNARXq72IlSic0oabg2FC1NU68L4RdHrNJDd8bFwrphYA==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/sha512": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", + "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==" + }, + "@stablelib/x25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", + "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "requires": { + "@stablelib/keyagreement": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/xsalsa20": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/xsalsa20/-/xsalsa20-1.0.2.tgz", + "integrity": "sha512-7XdBGbcNgBShmuhDXv1G1WPVCkjZdkb1oPMzSidO7Fve0MHntH6TjFkj5bfLI+aRE+61weO076vYpP/jmaAYog==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/salsa20": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "@taquito/http-utils": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-14.0.0.tgz", + "integrity": "sha512-ZWZzod/+/OEE26b9CnDRjHGfUKBJft3aXv/e/A9bTHAtvRNJqGIhofHcDg/jTaolBMarCF2b3XBYw35aOOSk4A==", + "requires": { + "axios": "^0.26.0" + } + }, + "@taquito/local-forging": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-14.0.0.tgz", + "integrity": "sha512-Nm0xGmS1Jzd+tU0a/8Y8XuTghbiPBgHDLo+e4141TK3OAwTzOw0an+w3xK9QVfzvxfIcZBSMjeMZzOwDdiqkJQ==", + "requires": { + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2" + } + }, + "@taquito/michel-codec": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-14.0.0.tgz", + "integrity": "sha512-ftnBvUVddlHBqvQbGPHEb26KrS4lIcaZ1eIpYJWiz+akb4Pcfyq7j/OEsDZbB7Pl2FP9hqu7ZygOF34zY6Lrtw==" + }, + "@taquito/michelson-encoder": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-14.0.0.tgz", + "integrity": "sha512-KIS+xl4rKfnd6hf9LUr6W+Pb7gv8F/Qsx0fho9CtM2PodKvdef3YlvkpScBUM9QZntAlvq2XQXUVXcZkbvxygw==", + "requires": { + "@taquito/rpc": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2", + "fast-json-stable-stringify": "^2.1.0" + } + }, + "@taquito/rpc": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-14.0.0.tgz", + "integrity": "sha512-FMfb80sA+VJwNx8OTNN07boDAt2roISqLLCUgmOIwy/cFDqhII7gdS4aYWJWqlKbdPKCPqh3a3ZQD1X/jyQHOA==", + "requires": { + "@taquito/http-utils": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2" + } + }, + "@taquito/signer": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-14.0.0.tgz", + "integrity": "sha512-vDqp/quzAsOiVikUt5MYUKhHI3S9qlasazyXs99xK9qpGLotbx6aseKcfb/dkaTo4/eoMzP4XzTVdnk0AqcCkw==", + "requires": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.2", + "@stablelib/nacl": "^1.0.3", + "@taquito/taquito": "^14.0.0", + "@taquito/utils": "^14.0.0", + "elliptic": "^6.5.4", + "pbkdf2": "^3.1.2", + "typedarray-to-buffer": "^4.0.0" + } + }, + "@taquito/taquito": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-14.0.0.tgz", + "integrity": "sha512-JaXfvqOCF3dkwXxhe00Ravb8WLMC2VqJxerf4GrGUEpJw+NAkwbGEb8k/52+MQQdlxMPepZdPPru/HM3nFG0Tw==", + "requires": { + "@taquito/http-utils": "^14.0.0", + "@taquito/local-forging": "^14.0.0", + "@taquito/michel-codec": "^14.0.0", + "@taquito/michelson-encoder": "^14.0.0", + "@taquito/rpc": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2", + "rxjs": "^6.6.3" + } + }, + "@taquito/utils": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-14.0.0.tgz", + "integrity": "sha512-0RSHn/CzbcbMdldJJIlXyxOvAajwmL6iPKJ6NaRyYJqqLM2CxYjG72KpXVv716pCMV1MbIWsOAr9FKbxW73PsA==", + "requires": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.2", + "@types/bs58check": "^2.1.0", + "bignumber.js": "^9.0.2", + "blakejs": "^1.2.1", + "bs58check": "^2.1.2", + "buffer": "^6.0.3", + "elliptic": "^6.5.4", + "typedarray-to-buffer": "^4.0.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, + "@types/bs58check": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.0.tgz", + "integrity": "sha512-OxsysnJQh82vy9DRbOcw9m2j/WiyqZLn0YBhKxdQ+aCwoHj+tWzyCgpwAkr79IfDXZKxc6h7k89T9pwS78CqTQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + }, + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, + "base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bignumber.js": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz", + "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==" + }, + "blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "typedarray-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz", + "integrity": "sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ==" + }, + "typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + } + } +} diff --git a/smartpy/bmc/package.json b/smartpy/bmc/package.json new file mode 100644 index 000000000..e24aa9572 --- /dev/null +++ b/smartpy/bmc/package.json @@ -0,0 +1,26 @@ +{ + "name": "create-tezos-app", + "version": "1.0.3", + "description": "A Tezos Dapp Starter using Typescript and Taquito.", + "bin": "./bin/cli.js", + "scripts": { + "compile": "bash ./compile.sh", + "deploy": "npx ts-node scripts/deploy.ts", + "test" : "bash ./test.sh" + }, + "author": "Roshan Parajuli", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/0xrpj/create-tezos-dapp" + }, + "dependencies": { + "@taquito/signer": "^14.0.0", + "@taquito/taquito": "^14.0.0", + "dotenv": "^16.0.3", + "ts-node": "^10.9.1" + }, + "devDependencies": { + "typescript": "^4.8.4" + } +} \ No newline at end of file diff --git a/smartpy/bmc/scripts/deploy.ts b/smartpy/bmc/scripts/deploy.ts new file mode 100644 index 000000000..42bceb58a --- /dev/null +++ b/smartpy/bmc/scripts/deploy.ts @@ -0,0 +1,62 @@ +import * as dotenv from "dotenv"; +import { TezosToolkit } from "@taquito/taquito"; +import { InMemorySigner } from "@taquito/signer"; +import { NETWORK } from "../config/config"; + +dotenv.config(); + +const deploy = async () => { + type networkTypes = keyof typeof NETWORK; + + const { ORIGINATOR_PRIVATE_KEY } = process.env; + let file = process.argv[2]; + let rpcType: networkTypes = process.argv[3] + ?.toUpperCase() + .slice(1) as networkTypes; + + console.info("Staring validation..."); + + if (!ORIGINATOR_PRIVATE_KEY) { + console.error("Private key missing in the environment."); + return; + } + + if (!file) { + console.log("Pass filename to deploy! Read the docs."); + return; + } + + if (!rpcType || !Object.keys(NETWORK).includes(rpcType)) { + console.log("Valid networks:", Object.keys(NETWORK)); + console.error("Invalid network"); + return; + } + + file = file.toLowerCase(); + + const signer = await InMemorySigner.fromSecretKey(ORIGINATOR_PRIVATE_KEY); + const Tezos = new TezosToolkit(NETWORK[rpcType].url); + Tezos.setProvider({ signer: signer }); + + console.log(`Validation checks out... \nDeploying the ${file} contract..\n`); + try { + const { hash, contractAddress } = await Tezos.contract.originate({ + code: require(`../contracts/build/${file}.json`), + init: require(`../contracts/build/${file}_storage.json`), + }); + + // console.log("Successfully deployed contract"); + // console.log(`>> Transaction hash: ${hash}`); + console.log(`::${contractAddress}`); + } catch (error) { + console.log( + `Oops... Deployment faced an issue.\nHere's the detailed info about the error,\n${error}` + ); + + console.log( + "Make sure of these things. \n1. Compiled contracts are inside the build folder.\n2. You have enough Tezos balance in the wallet for deployment." + ); + } +}; + +deploy(); diff --git a/smartpy/bmc/test.sh b/smartpy/bmc/test.sh new file mode 100755 index 000000000..d7235724e --- /dev/null +++ b/smartpy/bmc/test.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash + +set -e -o pipefail + +echo "----------------------------------------" +echo "Compiling contracts ... " +echo "----------------------------------------" + +# Expected location of SmartPy CLI. +SMART_PY_CLI=~/smartpy-cli/SmartPy.sh + +# Build artifact directory. +TEST_OUT_DIR=./contracts/build/.contract_build/test + +# Array of SmartPy files to compile. +# CONTRACTS_ARRAY=(counter) + +# Exit if SmartPy is not installed. +if [ ! -f "$SMART_PY_CLI" ]; then + echo "Fatal: Please install SmartPy CLI at $SMART_PY_CLI" && exit +fi + +function processContract { + CONTRACT_NAME=$1 + TEST_OUT_DIR=$2 + CONTRACT_IN_TEST="./contracts/tests/${CONTRACT_NAME}.py" + CONTRACT_OUT="${CONTRACT_NAME}.json" + STORAGE_OUT="${CONTRACT_NAME}_storage.json" + CONTRACT_COMPILED="${CONTRACT_NAME}/step_000_cont_0_contract.json" + STORAGE_COMPILED="${CONTRACT_NAME}/step_000_cont_0_storage.json" + + echo ">> Processing ${CONTRACT_NAME}" + + # Ensure file exists. + if [ ! -f "$CONTRACT_IN_TEST" ]; then + echo "Fatal: $CONTRACT_IN_TEST not found. Running from wrong dir?" && exit + fi + + echo ">>> Commencing the tests ${CONTRACT_NAME} ... " + $SMART_PY_CLI test $CONTRACT_IN_TEST $TEST_OUT_DIR --html +} + +export PYTHONPATH=$PWD + + +# Use if you want to pass a contract or more as arguments. +for n in $(seq 1 $#); do + processContract $1 $TEST_OUT_DIR + shift +done + +#!/usr/bin/env bash + +set -e -o pipefail + +echo "----------------------------------------" +echo "Compiling contracts ... " +echo "----------------------------------------" + +# Expected location of SmartPy CLI. +SMART_PY_CLI=~/smartpy-cli/SmartPy.sh + +# Build artifact directory. +TEST_OUT_DIR=./contracts/build/.contract_build/test + +# Array of SmartPy files to compile. +# CONTRACTS_ARRAY=(counter) + +# Exit if SmartPy is not installed. +if [ ! -f "$SMART_PY_CLI" ]; then + echo "Fatal: Please install SmartPy CLI at $SMART_PY_CLI" && exit +fi + +function processContract { + CONTRACT_NAME=$1 + TEST_OUT_DIR=$2 + CONTRACT_IN_TEST="./contracts/tests/${CONTRACT_NAME}_test.py" + CONTRACT_OUT="${CONTRACT_NAME}.json" + STORAGE_OUT="${CONTRACT_NAME}_storage.json" + CONTRACT_COMPILED="${CONTRACT_NAME}/step_000_cont_0_contract.json" + STORAGE_COMPILED="${CONTRACT_NAME}/step_000_cont_0_storage.json" + + echo ">> Processing ${CONTRACT_NAME}" + + # Ensure file exists. + if [ ! -f "$CONTRACT_IN_TEST" ]; then + echo "Fatal: $CONTRACT_IN_TEST not found. Running from wrong dir?" && exit + fi + + echo ">>> Commencing the tests ${CONTRACT_NAME} ... " + $SMART_PY_CLI test $CONTRACT_IN_TEST $TEST_OUT_DIR --html +} + +export PYTHONPATH=$PWD + + +# Use if you want to pass a contract or more as arguments. +for n in $(seq 1 $#); do + processContract $1 $TEST_OUT_DIR + shift +done + diff --git a/smartpy/bts/compile.sh b/smartpy/bts/compile.sh new file mode 100755 index 000000000..8d5d49f4f --- /dev/null +++ b/smartpy/bts/compile.sh @@ -0,0 +1,77 @@ + +#!/usr/bin/env bash + +set -e -o pipefail + +echo "----------------------------------------" +echo "Compiling contracts ... " +echo "----------------------------------------" + +# Expected location of SmartPy CLI. +SMART_PY_CLI=~/smartpy-cli/SmartPy.sh + +# Build artifact directory. +OUT_DIR=./contracts/build/.contract_build + +# Array of SmartPy files to compile. +# CONTRACTS_ARRAY=(counter) + +# Exit if SmartPy is not installed. +if [ ! -f "$SMART_PY_CLI" ]; then + echo "Fatal: Please install SmartPy CLI at $SMART_PY_CLI" && exit +fi + +function processContract { + CONTRACT_NAME=$1 + OUT_DIR=$2 + CONTRACT_IN="./contracts/src/${CONTRACT_NAME}.py" + CONTRACT_OUT="${CONTRACT_NAME}.json" + STORAGE_OUT="${CONTRACT_NAME}_storage.json" + CONTRACT_COMPILED="${CONTRACT_NAME}/step_000_cont_0_contract.json" + STORAGE_COMPILED="${CONTRACT_NAME}/step_000_cont_0_storage.json" + + echo ">> Processing ${CONTRACT_NAME}" + + # Ensure file exists. + if [ ! -f "$CONTRACT_IN" ]; then + echo "Fatal: $CONTRACT_IN not found. Running from wrong dir?" && exit + fi + +# echo ">>> [1 / 3] Testing ${CONTRACT_NAME} ... " +# $SMART_PY_CLI test $CONTRACT_IN $OUT_DIR --html + + echo ">>> [1 / 2] Compiling ${CONTRACT_NAME} ..." + $SMART_PY_CLI compile $CONTRACT_IN $OUT_DIR --html + + echo ">>> [2 / 2] Extracting Michelson contract ... " + cp $OUT_DIR/$CONTRACT_COMPILED ./contracts/build/$CONTRACT_OUT + cp $OUT_DIR/$STORAGE_COMPILED ./contracts/build/$STORAGE_OUT + + echo ">>> Michelson contract written to ${CONTRACT_OUT}" +} + +export PYTHONPATH=$PWD + + +echo "> [1 / 2] Compiling Contracts." +# Use if you want to pass a contract or more as arguments. +for n in $(seq 1 $#); do + processContract $1 $OUT_DIR + shift +done + +# Use if you want to compile all contracts in CONTRACTS_ARRAY. No arguments needed. +# for i in ${!CONTRACTS_ARRAY[@]}; do +# processContract ${CONTRACTS_ARRAY[$i]} $OUT_DIR +# done + +# Remove build artifacts. +echo "> [2 / 2] Cleaning up ..." +rm -rf $OUT_DIR +rm -rf ./contracts/__pycache__ +rm -rf ./__pycache__ + + +echo "> Removed artifacts." + +echo "> Compilation successful." \ No newline at end of file diff --git a/smartpy/bts/config/config.ts b/smartpy/bts/config/config.ts new file mode 100644 index 000000000..2dc56ea2d --- /dev/null +++ b/smartpy/bts/config/config.ts @@ -0,0 +1,20 @@ +// List your config files here + +export const NETWORK = { + GHOSTNET: { + name: "ghostnet", + url: "https://ghostnet.smartpy.io", + }, + KATHMANDUNET: { + name: "kathmandunet", + url: "https://kathmandunet.smartpy.io", + }, + JAKARTANET: { + name: "jakartanet", + url: "https://jakartanet.smartpy.io", + }, + MAINNET: { + name: "mainnet", + url: "https://mainnet.smartpy.io", + }, +}; diff --git a/smartpy/bts/contracts/src/FA2_contract.py b/smartpy/bts/contracts/src/FA2_contract.py new file mode 100644 index 000000000..858ce9baa --- /dev/null +++ b/smartpy/bts/contracts/src/FA2_contract.py @@ -0,0 +1,150 @@ +import smartpy as sp + +FA2 = sp.io.import_script_from_url("https://legacy.smartpy.io/templates/fa2_lib.py") +ZERO_ADDRESS = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg") + +t_transfer_batch = sp.TRecord( + callback=sp.TContract( + sp.TRecord(string=sp.TOption(sp.TString), requester=sp.TAddress, coin_name=sp.TString, value=sp.TNat)), + from_=sp.TAddress, + coin_name=sp.TString, + txs=sp.TList( + sp.TRecord( + to_=sp.TAddress, + token_id=sp.TNat, + amount=sp.TNat, + ).layout(("to_", ("token_id", "amount"))) + ), +).layout((("from_", "coin_name"), ("callback", "txs"))) + +t_transfer_params = sp.TList(t_transfer_batch) + + +class SingleAssetToken(FA2.Admin, FA2.Fa2SingleAsset, FA2.MintSingleAsset, FA2.BurnSingleAsset, + FA2.OnchainviewBalanceOf): + def __init__(self, admin, metadata, token_metadata): + FA2.Fa2SingleAsset.__init__(self, metadata=metadata, token_metadata=token_metadata) + FA2.Admin.__init__(self, admin) + self.update_initial_storage( + allowances=sp.big_map(tkey=sp.TRecord(spender=sp.TAddress, owner=sp.TAddress), tvalue=sp.TNat) + ) + + @sp.onchain_view() + def is_admin(self, address): + sp.result(address == self.data.administrator) + + @sp.entry_point + def set_allowance(self, param): + sp.set_type(param, sp.TRecord(spender=sp.TAddress, amount=sp.TNat)) + + sp.verify(param.spender != ZERO_ADDRESS, "Spender cannot be negative.") + allowance = sp.compute(sp.record(spender=param.spender, owner=sp.sender)) + self.data.allowances[allowance] = param.amount + + sp.emit(sp.record(owner=sp.sender, spender=param.spender, amount=param.amount), + tag="AllowanceSet") + + @sp.onchain_view() + def get_allowance(self, allowance): + sp.set_type(allowance, sp.TRecord(spender=sp.TAddress, owner=sp.TAddress)) + sp.result(self.data.allowances.get(allowance, default_value=0)) + + @sp.onchain_view() + def transfer_permissions(self, params): + sp.set_type(params, sp.TRecord(from_=sp.TAddress, token_id=sp.TNat)) + + with sp.if_((self.policy.supports_transfer) & (self.is_defined(params.token_id))): + with sp.if_((sp.sender == params.from_) | (self.data.operators.contains( + sp.record(owner=params.from_, operator=sp.sender, token_id=params.token_id)))): + sp.result(True) + with sp.else_(): + sp.result(False) + with sp.else_(): + sp.result(False) + + @sp.entry_point + def transfer(self, batch): + """Accept a list of transfer operations between a source and multiple + destinations. + Custom version with allowance system. + `transfer_tx_` must be defined in the child class. + """ + sp.set_type(batch, FA2.t_transfer_params) + if self.policy.supports_transfer: + with sp.for_("transfer", batch) as transfer: + with sp.for_("tx", transfer.txs) as tx: + # The ordering of sp.verify is important: 1) token_undefined, 2) transfer permission 3) balance + sp.verify(self.is_defined(tx.token_id), "FA2_TOKEN_UNDEFINED") + self.policy.check_tx_transfer_permissions( + self, transfer.from_, tx.to_, tx.token_id + ) + with sp.if_(sp.sender != transfer.from_): + self.update_allowance_(sp.sender, transfer.from_, tx.token_id, tx.amount) + with sp.if_(tx.amount > 0): + self.transfer_tx_(transfer.from_, tx) + else: + sp.failwith("FA2_TX_DENIED") + + def update_allowance_(self, spender, owner, token_id, amount): + allowance = sp.record(spender=spender, owner=owner) + self.data.allowances[allowance] = sp.as_nat(self.data.allowances.get(allowance, default_value=0) - amount, + message=sp.pair("FA2_NOT_OPERATOR", "NoAllowance")) + + +@sp.add_test(name="FA2Contract") +def test(): + alice = sp.test_account("Alice") + bob = sp.test_account("Bob") + spender = sp.test_account("spender") + receiver = sp.test_account("receiver") + + c1 = SingleAssetToken(admin=alice.address, metadata=sp.big_map({"ss": sp.bytes("0x0dae11")}), + token_metadata=sp.map({"ff": sp.bytes("0x0dae11")})) + + scenario = sp.test_scenario() + scenario.h1("FA2Contract") + scenario += c1 + + c1.mint([sp.record(to_=bob.address, amount=sp.nat(200))]).run(sender=alice) + scenario.verify(c1.data.ledger.get(bob.address) == sp.nat(200)) + + scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 0) + # set allowance + c1.set_allowance(sp.record(spender=spender.address, amount=sp.nat(100))).run(sender=bob) + + scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 100) + # increase allowance + c1.set_allowance(sp.record(spender=spender.address, amount=sp.nat(100))).run(sender=bob) + # verify new allowance + scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 100) + # decrease allowance + c1.set_allowance(sp.record(spender=spender.address, amount=sp.nat(20))).run(sender=bob) + # verify new allowance + scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 20) + # decrease allowance + c1.set_allowance(sp.record(spender=spender.address, amount=sp.nat(0))).run(sender=bob) + # verify new allowance + scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 0) + + c1.update_operators( + [sp.variant("add_operator", sp.record(owner=bob.address, operator=spender.address, token_id=0))]).run( + sender=bob) + # transfer more than allowance + # c1.transfer([sp.record(callback=sp.self_entry_point("callback"), from_=bob.address, txs=[sp.record(to_=receiver.address, token_id=0, amount=101)])]).run( + # sender=spender, valid=False, exception=('FA2_NOT_OPERATOR', 'NoAllowance')) + # # transfer all allowance + # c1.transfer([sp.record(from_=bob.address, txs=[sp.record(to_=receiver.address, token_id=0, amount=100)])]).run( + # sender=spender) + + # # verify remaining allowance + # scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 0) + # # again set allowance after prev-allowance is 0 + # c1.set_allowance([sp.record(spender=spender.address, amount=sp.nat(100))]).run(sender=bob) + + +sp.add_compilation_target("FA2_contract", + SingleAssetToken( + admin=sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP"), + metadata=sp.utils.metadata_of_url( + "ipfs://example"), + token_metadata=FA2.make_metadata(name="NativeWrappedCoin", decimals=6, symbol="WTEZ"))) diff --git a/smartpy/bts/contracts/src/RLP_struct.py b/smartpy/bts/contracts/src/RLP_struct.py new file mode 100644 index 000000000..9c826ffaf --- /dev/null +++ b/smartpy/bts/contracts/src/RLP_struct.py @@ -0,0 +1,315 @@ +import smartpy as sp + +Utils2 = sp.io.import_script_from_url("https://raw.githubusercontent.com/RomarQ/tezos-sc-utils/main/smartpy/utils.py") +types = sp.io.import_script_from_url("file:./contracts/src/Types.py") + + +class DecodeEncodeLibrary: + + def decode_response(self, rlp): + sp.set_type(rlp, sp.TBytes) + + code = sp.local("code_bts_decode_response", 0) + is_error = sp.local("error_in_bts_decode_response", sp.string("Success")) + rlp_message_list = sp.local("rlp_message_list_bts", sp.map(tkey=sp.TNat)) + is_list_lambda = sp.view("is_list", self.data.helper, rlp, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + rlp_message_list.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + rlp_ = rlp_message_list.value + counter = sp.local("counter_response_bts", 0) + msg = sp.local("message_in_bts", sp.string("")) + with sp.if_(is_error.value == "Success"): + sp.for i in rlp_.items(): + sp.if counter.value == 0: + code.value = Utils2.Int.of_bytes(i.value) + sp.if counter.value == 1: + msg.value = sp.view("decode_string", self.data.helper, i.value, t=sp.TString).open_some() + counter.value = counter.value + 1 + return sp.record(rv = sp.record(code=code.value, message = msg.value),status = is_error.value) + + def decode_service_message(self, rlp): + sp.set_type(rlp, sp.TBytes) + + rlp_service_message = sp.local("rlp_decode_service_message_bts", sp.map(tkey=sp.TNat)) + is_error = sp.local("error_in_bts_decode_service_message_bts", sp.string("Success")) + is_list_lambda = sp.view("is_list", self.data.helper, rlp, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + rlp_service_message.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + _service_type = sp.local("_service_type_decode_service_message_bts", sp.variant("ERROR", sp.nat(10))) + data = sp.local("data_decode_service_message_bts", sp.bytes("0x")) + with sp.if_(is_error.value == "Success"): + rlp_ = rlp_service_message.value + temp_int = sp.local("temp_int_decode_service_message_bts", 0) + counter = sp.local("counter_decode_service_message_bts", 0) + sp.for i in rlp_.items(): + sp.if counter.value == 0: + temp_int.value = Utils2.Int.of_bytes(i.value) + sp.if counter.value == 1: + data.value = sp.view("without_length_prefix", self.data.helper, i.value, + t=sp.TBytes).open_some() + counter.value = counter.value + 1 + + with sp.if_ (temp_int.value == 0): + _service_type.value = sp.variant("REQUEST_COIN_TRANSFER", temp_int.value) + with sp.if_ (temp_int.value == 1): + _service_type.value = sp.variant("REQUEST_COIN_REGISTER", temp_int.value) + with sp.if_ (temp_int.value == 2): + _service_type.value = sp.variant("RESPONSE_HANDLE_SERVICE", temp_int.value) + with sp.if_ (temp_int.value == 3): + _service_type.value = sp.variant("BLACKLIST_MESSAGE", temp_int.value) + with sp.if_ (temp_int.value == 4): + _service_type.value = sp.variant("CHANGE_TOKEN_LIMIT", temp_int.value) + with sp.if_ (temp_int.value == 5): + _service_type.value = sp.variant("UNKNOWN_TYPE", temp_int.value) + + return sp.record(rv=sp.record(serviceType=_service_type.value, + data=data.value), status=is_error.value) + + def decode_transfer_coin_msg(self, rlp): + sp.set_type(rlp, sp.TBytes) + + is_error = sp.local("error_in_bts_decode_transfer_coin_msg", sp.string("Success")) + rlp_transfer_coin = sp.local("rlp_transfer_coin_msg_bts", sp.map(tkey=sp.TNat)) + is_list_lambda = sp.view("is_list", self.data.helper, rlp, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + rlp_transfer_coin.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + rlp_ = rlp_transfer_coin.value + + temp_byt = sp.local("byt_transfer_coin_msg_bts", sp.bytes("0x")) + counter = sp.local("counter_transfer_coin_msg_bts", sp.nat(0)) + from_address = sp.local("from_address_transfer_coin_msg_bts", sp.string("")) + to_address = sp.local("to_address_transfer_coin_msg_bts", sp.string("")) + rv_assets = sp.local("assets", {}, sp.TMap(sp.TNat, types.Types.Asset)) + with sp.if_(is_error.value == "Success"): + sp.for i in rlp_.items(): + with sp.if_ (counter.value == 2): + temp_byt.value = i.value + with sp.if_ (counter.value == 0): + from_address.value = sp.view("decode_string", self.data.helper, i.value, t=sp.TString).open_some() + with sp.if_ (counter.value == 1): + to_address.value = sp.view("decode_string", self.data.helper, i.value, t=sp.TString).open_some() + counter.value = counter.value + 1 + sub_list = sp.local("sub_list_transfer_coin_msg_bts", temp_byt.value) + new_sub_list_tcm = sp.local("nsl_transfer_coin_msg_bts", sp.map(tkey=sp.TNat)) + is_list_lambda = sp.view("is_list", self.data.helper, sub_list.value, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + new_sub_list_tcm.value = sp.view("decode_list", self.data.helper, sub_list.value, + t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + with sp.if_(is_error.value == "Success"): + new_sub_list = new_sub_list_tcm.value + counter.value = 0 + new_temp_byt = sp.local("new_temp_byt_transfer_coin_msg_bts", sp.bytes("0x")) + nsl3_tcm = sp.local("nsl3_transfer_coin_msg_bts", sp.map(tkey=sp.TNat)) + view_value = sp.local("view_value_transfer_coin_msg_bts", sp.map(tkey=sp.TNat)) + counter_nested = sp.local("counter_nested_transfer_coin_msg_bts", sp.nat(0), t=sp.TNat) + temp_byt = sp.local("temp_byte2_transfer_coin_msg_bts", sp.bytes("0x")) + temp_byt_nested = sp.local("temp_byte_nested_transfer_coin_msg_bts", sp.bytes("0x")) + sp.for x in new_sub_list.items(): + new_temp_byt.value = x.value + is_list_lambda = sp.view("is_list", self.data.helper, new_temp_byt.value, + t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + nsl3_tcm.value = sp.view("decode_list", self.data.helper, new_temp_byt.value, + t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + with sp.if_(is_error.value == "Success"): + view_value.value = nsl3_tcm.value + counter_nested.value = sp.nat(0) + sp.for i in view_value.value.items(): + with sp.if_ (counter_nested.value == 1): + temp_byt_nested.value = sp.view("without_length_prefix", self.data.helper, i.value, + t=sp.TBytes).open_some() + with sp.if_ (counter_nested.value == 0): + temp_byt.value = i.value + counter_nested.value += 1 + + rv_assets.value[counter.value] = sp.record(coin_name=sp.view("decode_string", self.data.helper, temp_byt.value, t=sp.TString).open_some() + , value=Utils2.Int.of_bytes(temp_byt_nested.value)) + + counter.value = counter.value + 1 + + return sp.record(value=sp.record(from_addr= from_address.value, to = to_address.value, assets = rv_assets.value), status = is_error.value) + + def decode_blacklist_msg(self, rlp): + sp.set_type(rlp, sp.TBytes) + + rlp_blacklist_msg = sp.local("rlp_blacklist_msg_bts", sp.map(tkey=sp.TNat)) + is_error = sp.local("error_in_bts_decode_blacklist_message", sp.string("Success")) + is_list_lambda = sp.view("is_list", self.data.helper, rlp, t=sp.TBool).open_some() + _service_type = sp.local("_service_type_blacklist_msg_bts", sp.variant("ERROR", sp.nat(10))) + rv_blacklist_address = sp.local("blacklist_data", [], sp.TList(sp.TString)) + with sp.if_(is_list_lambda): + rlp_blacklist_msg.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + rlp_ = rlp_blacklist_msg.value + + temp_byt = sp.local("byt_transfer", sp.bytes("0x")) + rv1_byt = sp.local("rv1_byt", sp.bytes("0x")) + rv2_byt = sp.local("rv2_byt", sp.bytes("0x")) + counter = sp.local("counter_blacklist", sp.nat(0)) + with sp.if_(is_error.value == "Success"): + sp.for i in rlp_.items(): + sp.if counter.value == 2: + rv2_byt.value = i.value + rv2 = sp.view("decode_string", self.data.helper, rv2_byt.value, t=sp.TString).open_some() + sp.if counter.value == 0: + rv1_byt.value = sp.view("without_length_prefix", self.data.helper, i.value, + t=sp.TBytes).open_some() + rv1 = Utils2.Int.of_bytes(rv1_byt.value) + with sp.if_(rv1 == 0): + _service_type.value = sp.variant("ADD_TO_BLACKLIST", rv1) + with sp.else_(): + _service_type.value = sp.variant("REMOVE_FROM_BLACKLIST", rv1) + sp.if counter.value == 1: + temp_byt.value = i.value + counter.value = counter.value + 1 + sub_list = sp.local("sub_list", temp_byt.value) + nsl_bm = sp.local("nsl_bts_bm", sp.map(tkey=sp.TNat)) + is_list_lambda = sp.view("is_list", self.data.helper, sub_list.value, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + nsl_bm.value = sp.view("decode_list", self.data.helper, sub_list.value, + t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + with sp.if_(is_error.value == "Success"): + new_sub_list = nsl_bm.value + counter.value = 0 + addr_string = sp.local("addr_string", "") + counter.value = 0 + sp.for x in new_sub_list.items(): + addr_string.value = sp.view("decode_string", self.data.helper, x.value, + t=sp.TString).open_some() + rv_blacklist_address.value.push(addr_string.value) + counter.value = counter.value + 1 + + return sp.record(rv=sp.record(serviceType = _service_type.value , addrs = rv_blacklist_address.value , + net = rv2), status = is_error.value) + + def decode_token_limit_msg(self, rlp): + sp.set_type(rlp, sp.TBytes) + + rlp_tlm = sp.local("rlp_tlm_bts", sp.map(tkey=sp.TNat)) + is_list_lambda = sp.view("is_list", self.data.helper, rlp, t=sp.TBool).open_some() + is_error = sp.local("error_in_bts_decode_token_limit_msg", sp.string("Success")) + with sp.if_(is_list_lambda): + rlp_tlm.value = sp.view("decode_list", self.data.helper, rlp, t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + rlp_ = rlp_tlm.value + + temp_byt = sp.local("byt_transfer", sp.bytes("0x")) + temp_byt1 = sp.local("byt_transfer_temp1", sp.bytes("0x")) + rv1_byt = sp.local("rv1_byt", sp.bytes("0x")) + counter = sp.local("counter_token_limit", sp.nat(0)) + net = sp.local("network", sp.string("")) + rv_names = sp.local("names", {}, sp.TMap(sp.TNat, sp.TString)) + rv_limit = sp.local("limit", {}, sp.TMap(sp.TNat, sp.TNat)) + coin_name_limit = sp.local("coin_name_limit", {}, sp.TMap(sp.TString, sp.TNat)) + with sp.if_(is_error.value == "Success"): + sp.for i in rlp_.items(): + sp.if counter.value == 0: + temp_byt.value = i.value + sp.if counter.value == 1: + temp_byt1.value = i.value + sp.if counter.value == 2: + rv1_byt.value = i.value + counter.value = counter.value + 1 + sub_list = sp.local("sub_list", temp_byt.value) + net.value = sp.view("decode_string", self.data.helper, rv1_byt.value, t=sp.TString).open_some() + sub_list_limit = sp.local("sub_list_limit", temp_byt1.value) + nsl1_dtlm = sp.local("nsl1_bts_dtlm", sp.map(tkey=sp.TNat)) + is_list_lambda = sp.view("is_list", self.data.helper, sub_list.value, t=sp.TBool).open_some() + with sp.if_(is_list_lambda): + nsl1_dtlm.value = sp.view("decode_list", self.data.helper, sub_list.value, + t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + new_sub_list = nsl1_dtlm.value + counter.value = 0 + with sp.if_(is_error.value == "Success"): + sp.for x in new_sub_list.items(): + rv_names.value[counter.value] = sp.view("decode_string", self.data.helper, x.value, t=sp.TString).open_some() + counter.value += 1 + nsl_dtlm = sp.local("nsl_bts_dtlm", sp.map(tkey=sp.TNat)) + is_list_lambda = sp.view("is_list", self.data.helper, sub_list_limit.value, t=sp.TBool).open_some() + counter.value = 0 + with sp.if_(is_list_lambda): + nsl_dtlm.value = sp.view("decode_list", self.data.helper, sub_list_limit.value, + t=sp.TMap(sp.TNat, sp.TBytes)).open_some() + with sp.else_(): + is_error.value = "ErrorInBTSDecoding" + with sp.if_(is_error.value == "Success"): + new_sub_list1 = nsl_dtlm.value + limit = sp.local("limit_val", sp.bytes("0x"), t=sp.TBytes) + sp.for y in new_sub_list1.items(): + limit.value = sp.view("without_length_prefix", self.data.helper, y.value, + t=sp.TBytes).open_some() + rv_limit.value[counter.value] = Utils2.Int.of_bytes(limit.value) + counter.value += 1 + sp.for elem in sp.range(sp.nat(0), sp.len(rv_names.value)): + coin_name_limit.value[rv_names.value.get(elem)] = rv_limit.value.get(elem) + return sp.record(rv = sp.record(coin_name_limit = coin_name_limit.value, + net = net.value), status = is_error.value) + + # encoding starts here + + def encode_service_message(self, params): + sp.set_type(params, sp.TRecord(service_type_value=sp.TNat, data=sp.TBytes)) + + encode_service_type = sp.view("encode_nat", self.data.helper, params.service_type_value, + t=sp.TBytes).open_some() + rlp_bytes_with_prefix = sp.view("encode_list", self.data.helper, [encode_service_type, params.data], + t=sp.TBytes).open_some() + final_rlp_bytes_with_prefix = sp.view("with_length_prefix", self.data.helper, rlp_bytes_with_prefix, + t=sp.TBytes).open_some() + + return final_rlp_bytes_with_prefix + + def encode_transfer_coin_msg(self, data): + sp.set_type(data, sp.TRecord(from_addr=sp.TString, to=sp.TString, + assets=sp.TList(sp.TRecord(coin_name=sp.TString, value=sp.TNat, fee=sp.TNat)))) + + rlp = sp.local("rlp", sp.bytes("0x")) + rlp_list = sp.local("rlp_list", [], t=sp.TList(sp.TBytes)) + temp = sp.local("temp", sp.bytes("0x")) + coin_name = sp.local("coin_name", sp.bytes("0x")) + sp.for item in data.assets: + coin_name.value = sp.view("encode_string", self.data.helper, item.coin_name, t=sp.TBytes).open_some() + temp.value = sp.view("encode_nat", self.data.helper, item.value, t=sp.TBytes).open_some() + rlp_list.value.push( + sp.view("encode_list", self.data.helper, [coin_name.value, temp.value], t=sp.TBytes).open_some()) + # rlp.value = sp.view("with_length_prefix", self.data.helper, rlp.value, + # t=sp.TBytes).open_some() + + assets_list = sp.view("encode_list", self.data.helper, rlp_list.value, t=sp.TBytes).open_some() + from_addr_encoded = sp.view("encode_string", self.data.helper, data.from_addr, t=sp.TBytes).open_some() + to_addr_encoded = sp.view("encode_string", self.data.helper, data.to, t=sp.TBytes).open_some() + rlp.value = sp.view("encode_list", self.data.helper, [from_addr_encoded, to_addr_encoded, assets_list], + t=sp.TBytes).open_some() + final_rlp_bytes_with_prefix = sp.view("with_length_prefix", self.data.helper, rlp.value, + t=sp.TBytes).open_some() + + return final_rlp_bytes_with_prefix + + def encode_response(self, params): + sp.set_type(params, sp.TRecord(code=sp.TNat, message=sp.TString)) + + encode_code = sp.view("encode_nat", self.data.helper, params.code, t=sp.TBytes).open_some() + encode_message = sp.view("encode_string", self.data.helper, params.message, t=sp.TBytes).open_some() + + rlp_bytes_with_prefix = sp.view("encode_list", self.data.helper, [encode_code, encode_message], + t=sp.TBytes).open_some() + final_rlp_bytes_with_prefix = sp.view("with_length_prefix", self.data.helper, rlp_bytes_with_prefix, + t=sp.TBytes).open_some() + + return final_rlp_bytes_with_prefix \ No newline at end of file diff --git a/smartpy/bts/contracts/src/String.py b/smartpy/bts/contracts/src/String.py new file mode 100644 index 000000000..7968041c7 --- /dev/null +++ b/smartpy/bts/contracts/src/String.py @@ -0,0 +1,44 @@ +import smartpy as sp + +def split_btp_address(base): + """ + Split the BTP Address format i.e. btp://1234.iconee/0x123456789 + into Network_address (1234.iconee) and Server_address (0x123456789) + :param base: String base BTP Address format to be split + :return: The resulting strings of Network_address and Server_address + """ + sp.set_type(base, sp.TString) + + sep = sp.local("sep", "/") + prev_idx = sp.local('prev_idx', 0) + res = sp.local('res', []) + sp.for idx in sp.range(0, sp.len(base)): + sp.if sp.slice(base, idx, 1).open_some() == sep.value: + res.value.push(sp.slice(base, prev_idx.value, sp.as_nat(idx - prev_idx.value)).open_some()) + prev_idx.value = idx + 1 + sp.if sp.len(base) > 0: + res.value.push(sp.slice(base, prev_idx.value, sp.as_nat(sp.len(base) - prev_idx.value)).open_some()) + + inverted_list = sp.local("my_list", res.value) + last = sp.local("last", "") + penultimate = sp.local("penultimate", "") + + with sp.match_cons(inverted_list.value) as l: + last.value = l.head + inverted_list.value = l.tail + # with sp.else_(): + # sp.failwith("Empty list") + + + with sp.match_cons(inverted_list.value) as l: + penultimate.value = l.head + # with sp.else_(): + # sp.failwith("Only one element") + # TODO: should return true false + return sp.pair(penultimate.value, last.value) + + + + + + diff --git a/smartpy/bts/contracts/src/Types.py b/smartpy/bts/contracts/src/Types.py new file mode 100644 index 000000000..1a36f6cf2 --- /dev/null +++ b/smartpy/bts/contracts/src/Types.py @@ -0,0 +1,77 @@ +import smartpy as sp + + +class Types: + + Coin = sp.TRecord(addr=sp.TAddress, + fee_numerator=sp.TNat, + fixed_fee=sp.TNat, + coin_type=sp.TNat + ) + + Asset = sp.TRecord( + coin_name=sp.TString, + value=sp.TNat + ) + + AssetTransferDetail = sp.TRecord( + coin_name=sp.TString, + value=sp.TNat, + fee=sp.TNat + ) + + Response = sp.TRecord( + code=sp.TNat, + message=sp.TString + ) + + ServiceType = sp.TVariant( + REQUEST_COIN_TRANSFER=sp.TNat, + REQUEST_COIN_REGISTER=sp.TNat, + RESPONSE_HANDLE_SERVICE=sp.TNat, + BLACKLIST_MESSAGE=sp.TNat, + CHANGE_TOKEN_LIMIT=sp.TNat, + UNKNOWN_TYPE=sp.TNat, + ERROR=sp.TNat + ) + + BlacklistService = sp.TVariant( + ADD_TO_BLACKLIST=sp.TNat, + REMOVE_FROM_BLACKLIST=sp.TNat, + ERROR=sp.TNat + ) + + ServiceMessage = sp.TRecord( + serviceType=ServiceType, + data=sp.TBytes + ) + + TransferCoin = sp.TRecord( + from_addr=sp.TString, + to=sp.TString, + assets=sp.TList(Asset) + ) + + PendingTransferCoin = sp.TRecord( + from_=sp.TString, + to=sp.TString, + coin_details=sp.TList(sp.TRecord(coin_name=sp.TString, value=sp.TNat, fee=sp.TNat)) + ) + + BlacklistMessage = sp.TRecord( + serviceType=BlacklistService, + addrs=sp.TMap(sp.TNat, sp.TString), + net=sp.TString + ) + + TokenLimitMessage = sp.TRecord( + coin_name=sp.TMap(sp.TNat, sp.TString), + token_limit=sp.TMap(sp.TNat, sp.TNat), + net=sp.TString + ) + + Balance = sp.TRecord( + locked_balance=sp.TNat, + refundable_balance=sp.TNat + ) + diff --git a/smartpy/bts/contracts/src/bts_core.py b/smartpy/bts/contracts/src/bts_core.py new file mode 100644 index 000000000..df52fd6a5 --- /dev/null +++ b/smartpy/bts/contracts/src/bts_core.py @@ -0,0 +1,757 @@ +import smartpy as sp + +types = sp.io.import_script_from_url("file:./contracts/src/Types.py") +FA2_contract = sp.io.import_script_from_url("file:./contracts/src/FA2_contract.py") +t_balance_of_request = sp.TRecord(owner=sp.TAddress, token_id=sp.TNat).layout(("owner", "token_id")) +t_balance_of_response = sp.TRecord(request=t_balance_of_request, balance=sp.TNat).layout(("request", "balance")) + +class BTSCore(sp.Contract): + FEE_DENOMINATOR = sp.nat(10000) + RC_OK = sp.nat(0) + RC_ERR = sp.nat(1) + NATIVE_COIN_TYPE = sp.nat(0) + NATIVE_WRAPPED_COIN_TYPE = sp.nat(1) + NON_NATIVE_TOKEN_TYPE = sp.nat(2) + + MAX_BATCH_SIZE = sp.nat(15) + NATIVE_COIN_ADDRESS = sp.address("tz1burnburnburnburnburnburnburjAYjjX") + ZERO_ADDRESS = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg") + # Nat:(TWO.pow256 - 1) + UINT_CAP = sp.nat(115792089237316195423570985008687907853269984665640564039457584007913129639935) + + def __init__(self, _native_coin_name, _fee_numerator, _fixed_fee, owner_manager): + self.update_initial_storage( + bts_owner_manager=owner_manager, + bts_periphery_address=sp.none, + native_coin_name=_native_coin_name, + + coins_name=sp.list([_native_coin_name], t=sp.TString), + + aggregation_fee=sp.map({}, tkey=sp.TString, tvalue=sp.TNat), + balances=sp.big_map(tkey=sp.TRecord(address=sp.TAddress, coin_name=sp.TString), tvalue= types.Types.Balance), + coins=sp.map({_native_coin_name: self.NATIVE_COIN_ADDRESS}, tkey=sp.TString, tvalue=sp.TAddress), + coin_details=sp.map({_native_coin_name: sp.record(addr=self.NATIVE_COIN_ADDRESS, + fee_numerator=_fee_numerator, + fixed_fee=_fixed_fee, + coin_type=self.NATIVE_COIN_TYPE)}, + tkey=sp.TString, tvalue=types.Types.Coin), + coins_address=sp.map({self.NATIVE_COIN_ADDRESS: _native_coin_name}, tkey=sp.TAddress, tvalue=sp.TString), + is_paused=True + ) + + def only_owner(self): + is_owner = sp.view("is_owner", self.data.bts_owner_manager, sp.sender, t=sp.TBool).open_some( + "OwnerNotFound") + sp.verify(is_owner == True, message="Unauthorized") + + def only_bts_periphery(self): + sp.verify(sp.sender == self.data.bts_periphery_address.open_some("Address not set"), "Unauthorized") + + @sp.entry_point + def toggle_bridge_on(self): + self.only_owner() + with sp.if_(self.data.is_paused == False): + self.data.is_paused = True + with sp.else_(): + self.data.is_paused = False + + @sp.entry_point(lazify=False) + def update_update_bts_periphery(self, ep): + self.only_owner() + sp.set_entry_point("update_bts_periphery", ep) + + @sp.entry_point(lazify=True) + def update_bts_periphery(self, bts_periphery): + """ + update BTS Periphery address. + :param bts_periphery: BTSPeriphery contract address. + :return: + """ + sp.set_type(bts_periphery, sp.TAddress) + + self.only_owner() + with sp.if_(self.data.bts_periphery_address.is_some()): + has_requests = sp.view("has_pending_request", self.data.bts_periphery_address.open_some("Address not set"), + sp.unit, t=sp.TBool).open_some("has_pending_request not found") + sp.verify(has_requests == False, "HasPendingRequest") + self.data.bts_periphery_address = sp.some(bts_periphery) + + @sp.entry_point + def set_fee_ratio(self, name, fee_numerator, fixed_fee): + """ + set fee ratio. The transfer fee is calculated by fee_numerator/FEE_DEMONINATOR. + fee_numerator if it is set to `10`, which means the default fee ratio is 0.1%. + :param name: + :param fee_numerator: the fee numerator + :param fixed_fee: + :return: + """ + sp.set_type(name, sp.TString) + sp.set_type(fee_numerator, sp.TNat) + sp.set_type(fixed_fee, sp.TNat) + + self.only_owner() + sp.verify(fee_numerator < self.FEE_DENOMINATOR, message="InvalidSetting") + sp.verify((name == self.data.native_coin_name) | self.data.coins.contains(name), message = "TokenNotExists") + sp.verify((fixed_fee > sp.nat(0)) & (fee_numerator >= sp.nat(0)), message = "LessThan0") + self.data.coin_details[name].fee_numerator = fee_numerator + self.data.coin_details[name].fixed_fee = fixed_fee + + @sp.entry_point(lazify=False) + def update_register(self, ep): + self.only_owner() + sp.set_entry_point("register", ep) + + @sp.entry_point(lazify=True) + def register(self, name, fee_numerator, fixed_fee, addr, token_metadata, metadata): + """ + Registers a wrapped coin and id number of a supporting coin. + :param name: Must be different with the native coin name. + :param fee_numerator: + :param fixed_fee: + :param addr: address of the coin + :param token_metadata: Token metadata name, symbol and decimals of wrapped token + :param metadata: metadata of the token contract + :return: + """ + sp.set_type(name, sp.TString) + sp.set_type(fee_numerator, sp.TNat) + sp.set_type(fixed_fee, sp.TNat) + sp.set_type(addr, sp.TAddress) + sp.set_type(token_metadata, sp.TMap(sp.TString, sp.TBytes)) + sp.set_type(metadata, sp.TBigMap(sp.TString, sp.TBytes)) + self.only_owner() + sp.verify(name != self.data.native_coin_name, message="ExistNativeCoin") + sp.verify(self.data.coins.contains(name) == False, message= "ExistCoin") + sp.verify(self.data.coins_address.contains(addr) == False, message="AddressExists") + sp.verify(fee_numerator <= self.FEE_DENOMINATOR, message="InvalidSetting") + sp.verify((fixed_fee >= sp.nat(0)) & (fee_numerator >= sp.nat(0)), message="LessThan0") + with sp.if_(addr == self.ZERO_ADDRESS): + deployed_fa2 = sp.create_contract( + contract=FA2_contract.SingleAssetToken(admin=sp.self_address, + metadata=metadata, + token_metadata=token_metadata + )) + self.data.coins[name] = deployed_fa2 + self.data.coins_name.push(name) + self.data.coins_address[deployed_fa2] = name + self.data.coin_details[name] = sp.record( + addr = deployed_fa2, + fee_numerator = fee_numerator, + fixed_fee = fixed_fee, + coin_type = self.NATIVE_WRAPPED_COIN_TYPE + ) + with sp.else_(): + self.data.coins[name] = addr + self.data.coins_name.push(name) + self.data.coins_address[addr] = name + self.data.coin_details[name] = sp.record( + addr = addr, + fee_numerator = fee_numerator, + fixed_fee = fixed_fee, + coin_type = self.NON_NATIVE_TOKEN_TYPE + ) + + # token_map = sp.map({0:name}, tkey=sp.TNat, tvalue=sp.TString) + token_limit_map = sp.map({name:self.UINT_CAP}, tkey=sp.TString, tvalue=sp.TNat) + + # call set_token_limit on bts_periphery + set_token_limit_entry_point = sp.contract(sp.TMap(sp.TString, sp.TNat), + self.data.bts_periphery_address.open_some("Address not set"), + "set_token_limit").open_some("Error in set_token_limit call") + sp.transfer(token_limit_map, sp.tez(0), set_token_limit_entry_point) + + @sp.onchain_view() + def coin_id(self, coin_name): + """ + Return address of Coin whose name is the same with given coin_ame. + :param coin_name: + :return: An address of coin_name. + """ + sp.set_type(coin_name, sp.TString) + + sp.result(self.data.coins.get(coin_name)) + + @sp.onchain_view() + def native_coin_balance_of(self): + """ + Return balance of Native Coin . + :return: A Balance of native coin. + """ + sp.result(sp.pair(self.data.native_coin_name, sp.balance)) + + @sp.onchain_view() + def coin_type(self, coin_name): + """ + Return balance of Native Coin . + :return: A Balance of native coin. + """ + sp.set_type(coin_name, sp.TString) + sp.result(self.data.coin_details.get(coin_name).coin_type) + + @sp.onchain_view() + def is_valid_coin(self, coin_name): + """ + Check Validity of a coin_name + :param coin_name: + :return: true or false + """ + sp.set_type(coin_name, sp.TString) + + sp.result(self.data.coins.contains(coin_name)) + + @sp.onchain_view() + def fee_ratio(self, coin_name): + """ + Get fee numerator and fixed fee + :param coin_name: Coin name + :return: a record (Fee numerator for given coin, Fixed fee for given coin) + """ + sp.set_type(coin_name, sp.TString) + + coin = self.data.coin_details[coin_name] + fee_numerator = coin.fee_numerator + fixed_fee = coin.fixed_fee + sp.result(sp.record(fee_numerator =fee_numerator, fixed_fee = fixed_fee)) + + @sp.onchain_view() + def balance_of(self, params): + """ + Return a usable/locked/refundable balance of an account based on coinName. + usable_balance the balance that users are holding. + locked_balance when users transfer the coin,it will be locked until getting the Service Message Response. + refundable_balance refundable balance is the balance that will be refunded to users. + :param params: + :return: a record of (usable_balance, locked_balance, refundable_balance) + """ + sp.set_type(params, sp.TRecord(owner=sp.TAddress, coin_name=sp.TString)) + + # sending user_balance as 0 because in smartpy we cannot get Tez balance of address + locked_balance = sp.local("locked_balance", sp.nat(0)) + refundable_balance = sp.local("refundable_balance", sp.nat(0)) + + with sp.if_(self.data.balances.contains(sp.record(address=params.owner, coin_name=params.coin_name))): + locked_balance.value = self.data.balances[ + sp.record(address=params.owner, coin_name=params.coin_name)].locked_balance + refundable_balance.value = self.data.balances[ + sp.record(address=params.owner, coin_name=params.coin_name)].refundable_balance + + with sp.if_(params.coin_name == self.data.native_coin_name): + sp.result(sp.record(usable_balance=sp.nat(0), + locked_balance=locked_balance.value, + refundable_balance=refundable_balance.value, + user_balance=sp.nat(0))) + with sp.else_(): + fa2_address = self.data.coins.get(params.coin_name) + user_balance = sp.local("user_balance_onchain_view", sp.nat(0)) + usable_balance = sp.local("usable_balance", sp.nat(0), t=sp.TNat) + user_balance_of= sp.view("get_balance_of", fa2_address, [sp.record(owner=params.owner, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + sp.for elem in user_balance_of: + user_balance.value = elem.balance + with sp.if_(self.data.coin_details.get(params.coin_name).coin_type == self.NATIVE_WRAPPED_COIN_TYPE): + allowance = sp.view("get_allowance", fa2_address, + sp.record(spender=sp.self_address, owner=params.owner), t=sp.TNat).open_some("Invalid view") + usable_balance.value = allowance + with sp.if_(allowance > user_balance.value): + usable_balance.value = user_balance.value + + sp.result(sp.record(usable_balance=usable_balance.value, + locked_balance=locked_balance.value, + refundable_balance=refundable_balance.value, + user_balance=user_balance.value)) + + @sp.onchain_view() + def balance_of_batch(self, params): + """ + Return a list Balance of an account. + :param params: + :return: a record of (usableBalances, lockedBalances, refundableBalances) + """ + sp.set_type(params, sp.TRecord(owner=sp.TAddress, coin_names=sp.TList(sp.TString))) + + sp.verify((sp.len(params.coin_names) > sp.nat(0)) & (sp.len(params.coin_names) <= self.MAX_BATCH_SIZE), + message = "BatchMaxSizeExceed") + + balances= sp.local("balances_list", [], t=sp.TList(sp.TRecord(usable_balance=sp.TNat,locked_balance=sp.TNat, + refundable_balance=sp.TNat, user_balance=sp.TNat))) + sp.for item in params.coin_names: + balance= sp.view("balance_of", sp.self_address, + sp.record(owner=params.owner, coin_name=item)).open_some() + balances.value.push(balance) + sp.result(balances.value) + + @sp.onchain_view() + def get_accumulated_fees(self): + """ + Return a map with record of accumulated Fees. + :return: An map of Asset + """ + sp.result(self.data.aggregation_fee) + + @sp.entry_point(lazify=False) + def update_transfer_native_coin(self, ep): + self.only_owner() + sp.set_entry_point("transfer_native_coin", ep) + + @sp.entry_point(check_no_incoming_transfer=False, lazify=True) + def transfer_native_coin(self, to): + """ + Allow users to deposit `sp.amount` native coin into a BTSCore contract. + :param to: An address that a user expects to receive an amount of tokens. + :return: + """ + sp.set_type(to, sp.TString) + + with sp.if_(self.data.is_paused == True): + sp.failwith("Tezos bridge is paused.") + + amount_in_nat = sp.local("amount_in_nat", sp.utils.mutez_to_nat(sp.amount), t=sp.TNat) + # call check_transfer_restrictions on bts_periphery + check_transfer = sp.view("check_transfer_restrictions", + self.data.bts_periphery_address.open_some("Address not set"), + sp.record(coin_name=self.data.native_coin_name, user=sp.sender, + value=amount_in_nat.value), + t=sp.TBool).open_some() + sp.verify(check_transfer == True, "FailCheckTransfer") + + charge_amt = amount_in_nat.value * self.data.coin_details[self.data.native_coin_name].fee_numerator / \ + self.FEE_DENOMINATOR + self.data.coin_details[self.data.native_coin_name].fixed_fee + + self._send_service_message(sp.sender, to, self.data.native_coin_name, amount_in_nat.value, charge_amt) + + @sp.entry_point(lazify=False) + def update_transfer(self, ep): + self.only_owner() + sp.set_entry_point("transfer", ep) + + @sp.entry_point(lazify=True) + def transfer(self, coin_name, value, to): + """ + Allow users to deposit an amount of wrapped native coin `coin_name` from the `sp.sender` address into + the BTSCore contract. + :param coin_name: A given name of a wrapped coin + :param value: An amount request to transfer from a Requester (include fee) + :param to: Target BTP address. + :return: + """ + sp.set_type(coin_name, sp.TString) + sp.set_type(value, sp.TNat) + sp.set_type(to, sp.TString) + + with sp.if_(self.data.is_paused == True): + sp.failwith("Tezos bridge is paused.") + + sp.verify(coin_name != self.data.native_coin_name, message="InvalidWrappedCoin") + sp.verify(self.data.coins.contains(coin_name), message= "CoinNotRegistered") + fa2_address = self.data.coins[coin_name] + + # call check_transfer_restrictions on bts_periphery + check_transfer = sp.view("check_transfer_restrictions", + self.data.bts_periphery_address.open_some("Address not set"), + sp.record(coin_name=coin_name, user=sp.sender, value=value), + t=sp.TBool).open_some() + sp.verify(check_transfer == True, "FailCheckTransfer") + + charge_amt = value * self.data.coin_details[coin_name].fee_numerator / self.FEE_DENOMINATOR +\ + self.data.coin_details[coin_name].fixed_fee + + # call transfer from in FA2 + transfer_args_type = sp.TList(sp.TRecord(from_=sp.TAddress, txs=sp.TList(sp.TRecord( + to_=sp.TAddress, token_id=sp.TNat, amount=sp.TNat).layout(("to_", ("token_id", "amount")))) + ).layout(("from_", "txs"))) + transfer_entry_point = sp.contract(transfer_args_type, fa2_address, "transfer").open_some() + transfer_args = [sp.record(from_=sp.sender, txs=[sp.record(to_=sp.self_address, token_id=sp.nat(0), + amount=value)])] + sp.transfer(transfer_args, sp.tez(0), transfer_entry_point) + + self._send_service_message(sp.sender, to, coin_name, value, charge_amt) + + def _send_service_message(self, _from, to, coin_name, value, charge_amt): + """ + This private function handles overlapping procedure before sending a service message to BTSPeriphery + :param _from: An address of a Requester + :param to: BTP address of of Receiver on another chain + :param coin_name: A given name of a requested coin + :param value: A requested amount to transfer from a Requester (include fee) + :param charge_amt: An amount being charged for this request + :return: + """ + sp.set_type(_from, sp.TAddress) + sp.set_type(to, sp.TString) + sp.set_type(coin_name, sp.TString) + sp.set_type(value, sp.TNat) + sp.set_type(charge_amt, sp.TNat) + + sp.verify(value > charge_amt, message = "ValueGreaterThan0") + self._lock_balance(_from, coin_name, value) + coin_details = sp.local("coin_details_", [], t=sp.TList(sp.TRecord(coin_name=sp.TString, value=sp.TNat, + fee=sp.TNat))) + coin_details.value.push(sp.record(coin_name=coin_name, value=sp.as_nat(value - charge_amt), fee=charge_amt)) + + # call send_service_message on bts_periphery + send_service_message_args_type = sp.TRecord(_from=sp.TAddress, to=sp.TString, coin_details=sp.TList( + sp.TRecord(coin_name=sp.TString, value=sp.TNat, fee=sp.TNat))) + send_service_message_entry_point = sp.contract(send_service_message_args_type, + self.data.bts_periphery_address.open_some("Address not set"), "send_service_message").open_some() + send_service_message_args = sp.record(_from = _from, to = to, coin_details = coin_details.value) + sp.transfer(send_service_message_args, sp.tez(0), send_service_message_entry_point) + + @sp.entry_point(lazify=False) + def update_transfer_batch(self, ep): + self.only_owner() + sp.set_entry_point("transfer_batch", ep) + + @sp.entry_point(check_no_incoming_transfer=False, lazify=True) + def transfer_batch(self, coin_names_values, to): + """ + Allow users to transfer multiple coins/wrapped coins to another chain. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + In case of transferring a native coin, it also checks `msg.value` + The number of requested coins MUST be as the same as the number of requested values + The requested coins and values MUST be matched respectively + :param coin_names_values: A map of requested transferring wrapped coins and values + :param to: Target BTP address. + :return: + """ + sp.set_type(coin_names_values, sp.TMap(sp.TString, sp.TNat)) + sp.set_type(to, sp.TString) + + with sp.if_(self.data.is_paused == True): + sp.failwith("Tezos bridge is paused.") + + sp.verify(sp.len(coin_names_values) > sp.nat(0), message = "Zero length arguments") + + amount_in_nat = sp.local("amount_in_nat", sp.utils.mutez_to_nat(sp.amount), t=sp.TNat) + size = sp.local("size", sp.len(coin_names_values), t=sp.TNat) + with sp.if_(amount_in_nat.value != sp.nat(0)): + size.value = size.value + sp.nat(1) + + sp.verify(size.value <= self.MAX_BATCH_SIZE, message ="Batch maxSize Exceeds") + + batch_coin_details = sp.local("batch_coin_details", [], t=sp.TList(sp.TRecord(coin_name=sp.TString, + value=sp.TNat, fee=sp.TNat))) + charge_amt = sp.local("charge_amt__", sp.nat(0), t=sp.TNat) + amount = sp.local("amount__", sp.nat(0), t=sp.TNat) + sp.for item in coin_names_values.items(): + sp.verify(item.key != self.data.native_coin_name, message="InvalidCoin") + sp.verify(self.data.coins.contains(item.key), message="CoinNotRegistered") + fa2_address = self.data.coins[item.key] + + sp.verify(item.value > sp.nat(0), message="Cannot transfer less than or equal to zero") + + # call check_transfer_restrictions on bts_periphery + check_transfer = sp.view("check_transfer_restrictions", + self.data.bts_periphery_address.open_some("Address not set"), + sp.record(coin_name=item.key, user=sp.sender, value=item.value), + t=sp.TBool).open_some() + sp.verify(check_transfer == True, "FailCheckTransfer") + + # call transfer from in FA2 + transfer_args_type = sp.TList(sp.TRecord(from_=sp.TAddress, txs=sp.TList(sp.TRecord( + to_=sp.TAddress, token_id=sp.TNat, amount=sp.TNat).layout(("to_", ("token_id", "amount")))) + ).layout(("from_", "txs"))) + transfer_entry_point = sp.contract(transfer_args_type, fa2_address, "transfer").open_some() + transfer_args = [ + sp.record(from_=sp.sender, + txs=[sp.record(to_=sp.self_address, token_id=sp.nat(0), amount=item.value)])] + sp.transfer(transfer_args, sp.tez(0), transfer_entry_point) + + charge_amt.value = item.value * self.data.coin_details[item.key].fee_numerator / self.FEE_DENOMINATOR + \ + self.data.coin_details[item.key].fixed_fee + amount.value = sp.as_nat(item.value - charge_amt.value) + batch_coin_details.value.push(sp.record(coin_name=item.key, value=amount.value, fee=charge_amt.value)) + + self._lock_balance(sp.sender, item.key, item.value) + + with sp.if_(amount_in_nat.value !=sp.nat(0)): + # call check_transfer_restrictions on bts_periphery + check_transfer = sp.view("check_transfer_restrictions", + self.data.bts_periphery_address.open_some("Address not set"), + sp.record(coin_name=self.data.native_coin_name, user=sp.sender, + value=amount_in_nat.value), + t=sp.TBool).open_some() + sp.verify(check_transfer == True, "FailCheckTransfer") + + charge_amt.value = amount_in_nat.value * self.data.coin_details[self.data.native_coin_name].fee_numerator \ + / self.FEE_DENOMINATOR + self.data.coin_details[self.data.native_coin_name].fixed_fee + amount.value = sp.as_nat(amount_in_nat.value - charge_amt.value) + batch_coin_details.value.push(sp.record(coin_name=self.data.native_coin_name, value=amount.value, + fee=charge_amt.value)) + + self._lock_balance(sp.sender, self.data.native_coin_name, sp.utils.mutez_to_nat(sp.amount)) + + # call send_service_message on bts_periphery + send_service_message_args_type = sp.TRecord(_from=sp.TAddress, to=sp.TString, + coin_details=sp.TList(sp.TRecord(coin_name=sp.TString, + value=sp.TNat, fee=sp.TNat))) + send_service_message_entry_point = sp.contract(send_service_message_args_type, + self.data.bts_periphery_address.open_some("Address not set"), + "send_service_message").open_some() + send_service_message_args = sp.record(_from=sp.sender, to=to, coin_details=batch_coin_details.value) + sp.transfer(send_service_message_args, sp.tez(0), send_service_message_entry_point) + + @sp.entry_point(lazify=False) + def update_reclaim(self, ep): + self.only_owner() + sp.set_entry_point("reclaim", ep) + + @sp.entry_point(lazify=True) + #TODO: implement nonReentrant + def reclaim(self, coin_name, value): + """ + Reclaim the token's refundable balance by an owner. + The amount to claim must be smaller or equal than refundable balance + :param coin_name: A given name of coin + :param value: An amount of re-claiming tokens + :return: + """ + sp.set_type(coin_name, sp.TString) + sp.set_type(value, sp.TNat) + + with sp.if_(self.data.is_paused == True): + sp.failwith("Tezos bridge is paused.") + + record = sp.record(address=sp.sender,coin_name=coin_name) + with sp.if_(self.data.balances.contains(record)): + sp.verify(self.data.balances[record].refundable_balance >= value, message="Imbalance") + self.data.balances[record].refundable_balance = \ + sp.as_nat(self.data.balances[record].refundable_balance - value) + self.refund(sp.sender, coin_name, value) + with sp.else_(): + sp.failwith("NoRefundableBalance") + + def refund(self, to, coin_name, value): + """ + :param to: + :param coin_name: + :param value: + :return: + """ + sp.set_type(to, sp.TAddress) + sp.set_type(coin_name, sp.TString) + sp.set_type(value, sp.TNat) + + sp.verify(sp.sender == sp.self_address, message="Unauthorized") + + with sp.if_(coin_name == self.data.native_coin_name): + self.payment_transfer(to, value) + with sp.else_(): + # call transfer in FA2 + transfer_args_type = sp.TList(sp.TRecord(from_=sp.TAddress, txs=sp.TList(sp.TRecord( + to_=sp.TAddress, token_id=sp.TNat, amount=sp.TNat).layout(("to_", ("token_id", "amount")))) + ).layout(("from_", "txs"))) + transfer_entry_point = sp.contract(transfer_args_type, self.data.coins[coin_name], "transfer").open_some() + transfer_args = [sp.record(from_=sp.sender, txs=[sp.record(to_=to, token_id=sp.nat(0), amount=value)])] + sp.transfer(transfer_args, sp.tez(0), transfer_entry_point) + + def payment_transfer(self, to, amount): + sp.set_type(to, sp.TAddress) + sp.set_type(amount, sp.TNat) + + sp.send(to, sp.utils.nat_to_mutez(amount), message="PaymentFailed") + + @sp.entry_point(lazify=False) + def update_mint(self, ep): + self.only_owner() + sp.set_entry_point("mint", ep) + + @sp.entry_point(lazify=True) + def mint(self, to, coin_name, value): + """ + mint the wrapped coin. + :param to: the account receive the minted coin + :param coin_name: coin name + :param value: the minted amount + :return: + """ + sp.set_type(to, sp.TAddress) + sp.set_type(coin_name, sp.TString) + sp.set_type(value, sp.TNat) + + self.only_bts_periphery() + with sp.if_(coin_name == self.data.native_coin_name): + self.payment_transfer(to, value) + with sp.else_(): + _coin_type = self.data.coin_details[coin_name].coin_type + _coin_address = self.data.coins[coin_name] + with sp.if_(_coin_type == self.NATIVE_WRAPPED_COIN_TYPE): + # call mint in FA2 + mint_args_type = sp.TList(sp.TRecord(to_=sp.TAddress, amount=sp.TNat).layout(("to_", "amount"))) + mint_entry_point = sp.contract(mint_args_type, _coin_address, "mint").open_some() + mint_args = [sp.record(to_=to, amount=value)] + sp.transfer(mint_args, sp.tez(0), mint_entry_point) + with sp.else_(): + with sp.if_(_coin_type == self.NON_NATIVE_TOKEN_TYPE): + transfer_args_type = sp.TList(sp.TRecord(from_=sp.TAddress, txs=sp.TList(sp.TRecord( + to_=sp.TAddress, token_id=sp.TNat, amount=sp.TNat).layout(( + "to_", ("token_id", "amount"))))).layout(("from_", "txs")) + ) + transfer_entry_point = sp.contract(transfer_args_type, _coin_address, "transfer").open_some() + transfer_args = [ + sp.record(from_=sp.self_address, txs=[sp.record(to_=to, token_id=sp.nat(0), amount=value)])] + sp.transfer(transfer_args, sp.tez(0), transfer_entry_point) + + @sp.entry_point(lazify=False) + def update_handle_response_service(self, ep): + self.only_owner() + sp.set_entry_point("handle_response_service", ep) + + @sp.entry_point(lazify=True) + def handle_response_service(self, requester, coin_name, value, fee, rsp_code): + """ + Handle a response of a requested service. + :param requester: An address of originator of a requested service + :param coin_name: A name of requested coin + :param value: An amount to receive on a destination chain + :param fee: An amount of charged fee + :param rsp_code: + :return: + """ + sp.set_type(requester, sp.TAddress) + sp.set_type(coin_name, sp.TString) + sp.set_type(value, sp.TNat) + sp.set_type(fee, sp.TNat) + sp.set_type(rsp_code, sp.TNat) + + self.only_bts_periphery() + return_flag = sp.local("return_flag", False, t=sp.TBool) + bts_core_fa2_balance = sp.local("fa2_token_balance_core", sp.nat(0)) + + with sp.if_(requester == sp.self_address): + with sp.if_(rsp_code == self.RC_ERR): + self.data.aggregation_fee[coin_name] = self.data.aggregation_fee.get(coin_name, + default_value=sp.nat(0)) + value + return_flag.value = True + + with sp.if_(return_flag.value == False): + amount = sp.local("amount", value + fee, t=sp.TNat) + user= sp.record(address=requester, coin_name=coin_name) + _user_balances=self.data.balances.get(user,default_value=sp.record(locked_balance=sp.nat(0), + refundable_balance=sp.nat(0))) + + self.data.balances[user].locked_balance = sp.as_nat(_user_balances.locked_balance - amount.value) + + with sp.if_(rsp_code == self.RC_ERR): + with sp.if_(coin_name == self.data.native_coin_name): + # NATIVE COIN CASE + with sp.if_(sp.utils.mutez_to_nat(sp.balance) >= value): + self.payment_transfer(requester, value) + with sp.else_(): + self.data.balances[user].refundable_balance = _user_balances.refundable_balance + value + with sp.else_(): + _fa2_address = self.data.coins[coin_name] + with sp.if_(self.data.coin_details[coin_name].coin_type == self.NON_NATIVE_TOKEN_TYPE): + # call transfer in NON_NATIVE_TOKEN_TYPE FA2 + bts_core_fa2 = sp.view("get_balance_of", _fa2_address, + [sp.record(owner=sp.self_address, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + sp.for elem in bts_core_fa2: + bts_core_fa2_balance.value = elem.balance + with sp.if_(bts_core_fa2_balance.value >= value): + transfer_args_type = sp.TList(sp.TRecord(from_=sp.TAddress, txs=sp.TList(sp.TRecord( + to_=sp.TAddress, token_id=sp.TNat, amount=sp.TNat).layout( + ("to_", ("token_id", "amount"))))).layout(("from_", "txs")) + ) + transfer_entry_point = sp.contract(transfer_args_type, _fa2_address, "transfer").open_some() + transfer_args = [sp.record(from_=sp.self_address, txs=[ + sp.record(to_=requester, token_id=sp.nat(0), amount=value)])] + sp.transfer(transfer_args, sp.tez(0), transfer_entry_point) + with sp.else_(): + self.data.balances[user].refundable_balance = _user_balances.refundable_balance + value + with sp.else_(): + # Case of NATIVE_WRAPPED_COIN_TYPE + transfer_permissions = sp.view("transfer_permissions", _fa2_address, sp.record( + from_=sp.self_address, token_id=sp.nat(0)), t=sp.TBool).open_some() + + with sp.if_(transfer_permissions): + # call transfer in NATIVE_WRAPPED_COIN_TYPE FA2 + transfer_args_type = sp.TList(sp.TRecord( + from_=sp.TAddress, + txs=sp.TList(sp.TRecord(to_=sp.TAddress, token_id=sp.TNat, amount=sp.TNat).layout( + ("to_", ("token_id", "amount"))))).layout(("from_", "txs")) + ) + transfer_entry_point = sp.contract(transfer_args_type, _fa2_address, "transfer").open_some() + transfer_args = [ + sp.record(from_=sp.self_address, txs=[sp.record(to_=requester, token_id=sp.nat(0), + amount=value)])] + sp.transfer(transfer_args, sp.tez(0), transfer_entry_point) + with sp.else_(): + self.data.balances[user].refundable_balance = _user_balances.refundable_balance + value + + with sp.if_(rsp_code == self.RC_OK): + fa2_address = self.data.coins[coin_name] + with sp.if_((coin_name != self.data.native_coin_name) &\ + (self.data.coin_details[coin_name].coin_type == self.NATIVE_WRAPPED_COIN_TYPE)): + # call burn in FA2 + burn_args_type = sp.TList(sp.TRecord(from_=sp.TAddress, token_id=sp.TNat, amount=sp.TNat).layout( + ("from_", ("token_id", "amount")))) + burn_entry_point = sp.contract(burn_args_type, fa2_address, "burn").open_some() + burn_args = [sp.record(from_=sp.self_address, token_id=sp.nat(0), amount=value)] + sp.transfer(burn_args, sp.tez(0), burn_entry_point) + + self.data.aggregation_fee[coin_name] = self.data.aggregation_fee.get(coin_name, default_value=sp.nat(0))\ + + fee + + @sp.entry_point(lazify=False) + def update_transfer_fees(self, ep): + self.only_owner() + sp.set_entry_point("transfer_fees", ep) + + @sp.entry_point(lazify=True) + def transfer_fees(self, fa): + """ + Handle a request of Fee Gathering. Usage: Copy all charged fees to an array + :param fa: + :return: + """ + sp.set_type(fa, sp.TString) + + self.only_bts_periphery() + + transfer_fes_details = sp.local("transfer_fes_details", [], t=sp.TList(sp.TRecord(coin_name=sp.TString, + value=sp.TNat, fee=sp.TNat))) + sp.for item in self.data.coins_name: + with sp.if_(self.data.aggregation_fee.get(item, default_value=sp.nat(0)) != sp.nat(0)): + transfer_fes_details.value.push(sp.record(coin_name=item, value=self.data.aggregation_fee.get( + item, default_value=sp.nat(0)), fee=sp.nat(0))) + del self.data.aggregation_fee[item] + + # call send_service_message on bts_periphery + send_service_message_args_type = sp.TRecord(_from=sp.TAddress, to=sp.TString, + coin_details=sp.TList(sp.TRecord(coin_name=sp.TString, + value=sp.TNat, fee=sp.TNat))) + send_service_message_entry_point = sp.contract(send_service_message_args_type, + self.data.bts_periphery_address.open_some("Address not set"), + "send_service_message").open_some() + send_service_message_args = sp.record(_from=sp.self_address, to=fa, coin_details=transfer_fes_details.value) + sp.transfer(send_service_message_args, sp.tez(0), send_service_message_entry_point) + + + def _lock_balance(self, to, coin_name, value): + new_balance = self.data.balances.get(sp.record(address=to, coin_name=coin_name), + default_value=sp.record(locked_balance=sp.nat(0), + refundable_balance=sp.nat(0))) + self.data.balances[sp.record(address=to, coin_name=coin_name)] = sp.record( + locked_balance=new_balance.locked_balance + value, refundable_balance=new_balance.refundable_balance) + + @sp.entry_point + def set_bts_owner_manager(self, owner_manager): + sp.set_type(owner_manager, sp.TAddress) + + self.only_owner() + self.data.bts_owner_manager = owner_manager + + +sp.add_compilation_target("bts_core", BTSCore( + owner_manager=sp.address("KT1MxuVecS7HRRRZrJM7juddJg1HZZ4SGA5B"), + _native_coin_name="btp-NetXnHfVqm9iesp.tezos-XTZ", + _fee_numerator=sp.nat(100), + _fixed_fee=sp.nat(450) +)) + + + + + + diff --git a/smartpy/bts/contracts/src/bts_owner_manager.py b/smartpy/bts/contracts/src/bts_owner_manager.py new file mode 100644 index 000000000..68e98a08b --- /dev/null +++ b/smartpy/bts/contracts/src/bts_owner_manager.py @@ -0,0 +1,68 @@ +import smartpy as sp + + +class BTSOwnerManager(sp.Contract): + def __init__(self, owner): + self.init( + owners=sp.map({owner: True}), + ) + self.init_type(sp.TRecord( + owners=sp.TMap(sp.TAddress, sp.TBool), + )) + + def only_owner(self): + sp.verify(self.data.owners[sp.sender] == True, message="Unauthorized") + + @sp.entry_point + def add_owner(self, owner): + """ + :param owner: address to set as owner + :return: + """ + sp.set_type(owner, sp.TAddress) + + self.only_owner() + sp.verify(self.data.owners[owner] == False, message="ExistedOwner") + + self.data.owners[owner] = True + sp.emit(sp.record(sender=sp.sender, owner=owner), tag="NewOwnerAdded") + + @sp.entry_point + def remove_owner(self, owner): + """ + :param owner: address to remove as owner + :return: + """ + sp.set_type(owner, sp.TAddress) + + self.only_owner() + sp.verify(sp.len(self.data.owners) > 1, message="CannotRemoveMinOwner") + sp.verify(self.data.owners[owner] == True, message="NotOwner") + + del self.data.owners[owner] + sp.emit(sp.record(sender=sp.sender, owner=owner), tag="OwnerRemoved") + + @sp.onchain_view() + def is_owner(self, owner): + sp.set_type(owner, sp.TAddress) + sp.result(self.data.owners.get(owner, default_value=False)) + + @sp.onchain_view() + def get_owners(self): + sp.result(self.data.owners.keys()) + + +@sp.add_test(name="BTSOwnerManager") +def test(): + alice = sp.test_account("Alice") + c1 = BTSOwnerManager(alice.address) + scenario = sp.test_scenario() + scenario.h1("BTSOwnerManager") + scenario += c1 + + scenario.verify(c1.is_owner(alice.address) == True) + + +sp.add_compilation_target("bts_owner_manager", BTSOwnerManager(owner=sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP"))) + + diff --git a/smartpy/bts/contracts/src/bts_periphery.py b/smartpy/bts/contracts/src/bts_periphery.py new file mode 100644 index 000000000..97fc84eca --- /dev/null +++ b/smartpy/bts/contracts/src/bts_periphery.py @@ -0,0 +1,599 @@ +import smartpy as sp + +types = sp.io.import_script_from_url("file:./contracts/src/Types.py") +strings = sp.io.import_script_from_url("file:./contracts/src/String.py") +rlp = sp.io.import_script_from_url("file:./contracts/src/RLP_struct.py") +t_balance_of_request = sp.TRecord(owner=sp.TAddress, token_id=sp.TNat).layout(("owner", "token_id")) +t_balance_of_response = sp.TRecord(request=t_balance_of_request, balance=sp.TNat).layout(("request", "balance")) + +class BTSPeriphery(sp.Contract, rlp.DecodeEncodeLibrary): + service_name = sp.string("bts") + + RC_OK = sp.nat(0) + RC_ERR = sp.nat(1) + UINT_CAP = sp.nat(115792089237316195423570985008687907853269984665640564039457584007913129639935) + + MAX_BATCH_SIZE = sp.nat(15) + ZERO_ADDRESS = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg") + + def __init__(self, bmc_address, bts_core_address, helper_contract, parse_address, native_coin_name, owner_address): + self.update_initial_storage( + bmc=bmc_address, + owner=owner_address, + bts_core=bts_core_address, + blacklist=sp.map(tkey=sp.TAddress, tvalue=sp.TBool), + token_limit=sp.map({native_coin_name : self.UINT_CAP}, tkey=sp.TString, tvalue=sp.TNat), + requests=sp.big_map(tkey=sp.TInt, tvalue=types.Types.PendingTransferCoin), + serial_no = sp.int(0), + number_of_pending_requests = sp.nat(0), + helper=helper_contract, + parse_contract=parse_address + ) + + def only_bmc(self): + check_access = sp.local("check_access", "Unauthorized") + with sp.if_(sp.sender == self.data.bmc): + check_access.value = "Authorized" + return check_access.value + + def only_owner(self): + sp.verify(sp.sender == self.data.owner, "Unauthorized") + + def only_bts_core(self): + sp.verify(sp.sender == self.data.bts_core, "Unauthorized") + + @sp.entry_point + def set_helper_address(self, address): + sp.set_type(address, sp.TAddress) + self.only_owner() + self.data.helper = address + + @sp.entry_point + def set_parse_address(self, address): + sp.set_type(address, sp.TAddress) + self.only_owner() + self.data.parse_contract = address + + @sp.entry_point + def set_bmc_address(self, address): + sp.set_type(address, sp.TAddress) + self.only_owner() + self.data.bmc = address + + @sp.entry_point + def set_bts_core_address(self, address): + sp.set_type(address, sp.TAddress) + self.only_owner() + self.data.bts_core = address + + @sp.onchain_view() + def has_pending_request(self): + """ + :return: boolean + """ + sp.result(self.data.number_of_pending_requests != sp.nat(0)) + + # private function for adding blacklist address + def _add_to_blacklist(self, params): + """ + :param params: List of addresses to be blacklisted + :return: + """ + sp.set_type(params, sp.TList(sp.TString)) + + add_blacklist_status = sp.local("add_blacklist_status", "success") + addr_list = sp.local("addr_list", [], sp.TList(sp.TAddress)) + with sp.if_(sp.len(params) <= self.MAX_BATCH_SIZE): + sp.for item in params: + parsed_addr = sp.view("str_to_addr", self.data.parse_contract, item, t=sp.TAddress).open_some() + with sp.if_(add_blacklist_status.value == "success"): + with sp.if_(parsed_addr == self.ZERO_ADDRESS): + add_blacklist_status.value = "InvalidAddress" + with sp.else_(): + addr_list.value.push(parsed_addr) + with sp.if_(add_blacklist_status.value == "success"): + sp.for _item in addr_list.value: + self.data.blacklist[_item] = True + with sp.else_(): + add_blacklist_status.value = "InvalidAddress" + with sp.else_(): + add_blacklist_status.value = "error" + return add_blacklist_status.value + + # private function for removing blacklist address + def _remove_from_blacklist(self, params): + """ + :param params: list of address strings + :return: + """ + sp.set_type(params, sp.TList(sp.TString)) + + remove_blacklist_status = sp.local("remove_blacklist_status", "success") + addr_list = sp.local("addr_list", [], sp.TList(sp.TAddress)) + + with sp.if_(sp.len(params) <= self.MAX_BATCH_SIZE): + sp.for item in params: + parsed_addr = sp.view("str_to_addr", self.data.parse_contract, item, t=sp.TAddress).open_some() + with sp.if_(remove_blacklist_status.value == "success"): + with sp.if_((parsed_addr == self.ZERO_ADDRESS) | + (self.data.blacklist.contains(parsed_addr) == False)): + remove_blacklist_status.value = "InvalidAddress" + with sp.else_(): + addr_list.value.push(parsed_addr) + with sp.if_(remove_blacklist_status.value == "success"): + sp.for _item in addr_list.value: + del self.data.blacklist[_item] + with sp.else_(): + remove_blacklist_status.value = "InvalidAddress" + with sp.else_(): + remove_blacklist_status.value = "error" + return remove_blacklist_status.value + + @sp.entry_point + def set_token_limit(self, coin_names_limit): + """ + :param coin_names_limit: map of coin names and its limits + :return: + """ + sp.set_type(coin_names_limit, sp.TMap(sp.TString, sp.TNat)) + + sp.verify((sp.sender == self.data.bts_core), "Unauthorized") + sp.verify(sp.len(coin_names_limit) <= self.MAX_BATCH_SIZE, "BatchMaxSizeExceed") + + coin_names_limit_items = coin_names_limit.items() + sp.for item in coin_names_limit_items: + self.data.token_limit[item.key] = item.value + + # private function to return the tx status made from handle_btp_message + def _set_token_limit(self, coin_name_limit): + """ + :param coin_names: list of coin names + :param token_limit: list of token limits + :return: + """ + sp.set_type(coin_name_limit, sp.TMap(sp.TString, sp.TNat)) + + set_limit_status = sp.local("set_limit_status", "success") + with sp.if_(sp.len(coin_name_limit) <= self.MAX_BATCH_SIZE): + sp.for item in coin_name_limit.items(): + self.data.token_limit[item.key] = item.value + with sp.else_(): + set_limit_status.value = "error" + return set_limit_status.value + + @sp.entry_point(lazify=False) + def update_send_service_message(self, ep): + self.only_owner() + sp.set_entry_point("send_service_message", ep) + + @sp.entry_point(lazify=True) + def send_service_message(self, _from, to, coin_details): + """ + Send service message to BMC + :param _from: from address + :param to: to address + :param coin_details: + :return: + """ + + sp.set_type(_from, sp.TAddress) + sp.set_type(to, sp.TString) + sp.set_type(coin_details, sp.TList(sp.TRecord(coin_name=sp.TString, value=sp.TNat, fee=sp.TNat))) + + self.only_bts_core() + + to_network, to_address = sp.match_pair(strings.split_btp_address(to)) + + assets_details = sp.compute(sp.map(tkey=sp.TNat, tvalue=types.Types.AssetTransferDetail)) + + i=sp.local("i_", sp.nat(0)) + sp.for item in coin_details: + assets_details[i.value]= item + i.value += sp.nat(1) + + self.data.serial_no += 1 + + start_from = sp.view("add_to_str", self.data.parse_contract, _from, t=sp.TString).open_some() + + send_message_args_type = sp.TRecord(to=sp.TString, svc=sp.TString, sn=sp.TInt, msg=sp.TBytes) + send_message_entry_point = sp.contract(send_message_args_type, self.data.bmc, "send_message").open_some() + send_message_args = sp.record( + to=to_network, svc=self.service_name, sn=self.data.serial_no, + msg = self.encode_service_message(sp.record(service_type_value=sp.nat(0), + data=self.encode_transfer_coin_msg(sp.record(from_addr=start_from, to=to_address, + assets=coin_details)) + ) + ) + ) + + sp.transfer(send_message_args, sp.tez(0), send_message_entry_point) + + # push pending tx into record list + self.data.requests[self.data.serial_no] = sp.record( + from_=start_from, to=to, coin_details=coin_details) + self.data.number_of_pending_requests += sp.nat(1) + sp.emit(sp.record(from_address=_from, to=to, serial_no=self.data.serial_no, + assets_details=assets_details), tag="TransferStart") + + @sp.entry_point(lazify=False) + def update_handle_btp_message(self, ep): + self.only_owner() + sp.set_entry_point("handle_btp_message", ep) + + @sp.entry_point(lazify=True) + def handle_btp_message(self, _from, svc, sn, msg, callback, prev, callback_msg): + """ + BSH handle BTP message from BMC contract + :param _from: An originated network address of a request + :param svc: A service name of BSH contract + :param sn: A serial number of a service request + :param msg: An RLP message of a service request/service response + :param callback: callback function type in bmc_periphery + :param prev: param for callback function in bmc_periphery + :param callback_msg: param for callback function in bmc_periphery + :return: + """ + + sp.set_type(_from, sp.TString) + sp.set_type(svc, sp.TString) + sp.set_type(sn, sp.TInt) + sp.set_type(msg, sp.TBytes) + sp.set_type(callback, sp.TContract(sp.TRecord(string=sp.TOption(sp.TString), + prev=sp.TString, callback_msg=sp.TRecord(src=sp.TString, + dst=sp.TString, svc=sp.TString, sn=sp.TInt, message=sp.TBytes) + ))) + + check_caller = self.only_bmc() + + callback_string = sp.local("callback_string", "success") + with sp.if_((svc == self.service_name) & (check_caller == "Authorized")): + decode_call = self.decode_service_message(msg) + with sp.if_(decode_call.status == "Success"): + sm = decode_call.rv + + with sm.serviceType.match_cases() as arg: + with arg.match("REQUEST_COIN_TRANSFER") as a1: + tc_call = self.decode_transfer_coin_msg(sm.data) + with sp.if_(tc_call.status == "Success"): + tc = tc_call.value + parsed_addr = sp.view("str_to_addr", self.data.parse_contract, + tc.to, t=sp.TAddress).open_some() + + with sp.if_(parsed_addr != self.ZERO_ADDRESS): + handle_request_call= self._handle_request_service(parsed_addr, tc.assets) + # first param of send_response_message is service type value + with sp.if_(handle_request_call == "success"): + self.send_response_message(sp.nat(2), + _from, sn, "", self.RC_OK) + sp.emit(sp.record(from_address=_from, to=parsed_addr, serial_no=self.data.serial_no, + assets_details=tc.assets), tag="TransferReceived") + with sp.else_(): + self.send_response_message(sp.nat(2), + _from, sn, handle_request_call, self.RC_ERR) + with sp.else_(): + self.send_response_message(sp.nat(2), _from, + sn, "InvalidAddress", self.RC_ERR) + with sp.else_(): + callback_string.value = "ErrorInDecodingTransferCoin" + + with arg.match("BLACKLIST_MESSAGE") as a2: + bm_call = self.decode_blacklist_msg(sm.data) + with sp.if_(bm_call.status == "Success"): + bm = bm_call.rv + addresses = bm.addrs + + with bm.serviceType.match_cases() as b_agr: + with b_agr.match("ADD_TO_BLACKLIST") as b_val_1: + add_blacklist_call = self._add_to_blacklist(addresses) + with sp.if_(add_blacklist_call == "success"): + self.send_response_message(sp.nat(3), _from, + sn, "AddedToBlacklist", self.RC_OK) + with sp.else_(): + self.send_response_message(sp.nat(3), _from, + sn, "ErrorAddToBlackList", self.RC_ERR) + + with b_agr.match("REMOVE_FROM_BLACKLIST") as b_val_2: + remove_blacklist_call = self._remove_from_blacklist(addresses) + with sp.if_(remove_blacklist_call == "success"): + self.send_response_message(sp.nat(3), _from, + sn, "RemovedFromBlacklist", self.RC_OK) + with sp.else_(): + self.send_response_message(sp.nat(3), _from, + sn, "ErrorRemoveFromBlackList", self.RC_ERR) + + with b_agr.match("ERROR") as b_val_2: + self.send_response_message(sp.nat(3), _from, sn, + "BlacklistServiceTypeErr", self.RC_ERR) + with sp.else_(): + callback_string.value = "ErrorInDecodingBlacklist" + + with arg.match("CHANGE_TOKEN_LIMIT") as a3: + tl_call = self.decode_token_limit_msg(sm.data) + with sp.if_(tl_call.status == "Success"): + tl = tl_call.rv + coin_name_limit = tl.coin_name_limit + + set_limit_call = self._set_token_limit(coin_name_limit) + with sp.if_(set_limit_call == "success"): + self.send_response_message(sp.nat(4), _from, sn, + "ChangeTokenLimit", self.RC_OK) + with sp.else_(): + self.send_response_message(sp.nat(4), _from, sn, + "ErrorChangeTokenLimit", self.RC_ERR) + with sp.else_(): + callback_string.value = "ErrorInDecodingTokenLimit" + + with arg.match("RESPONSE_HANDLE_SERVICE") as a4: + with sp.if_(sp.len(self.data.requests.get(sn).from_) != 0): + fn_call = self.decode_response(sm.data) + response = fn_call.rv + with sp.if_(fn_call.status == "Success"): + handle_response = self.handle_response_service(sn, response.code, response.message) + with sp.if_(handle_response != "success"): + callback_string.value = handle_response + with sp.else_(): + callback_string.value = "ErrorInDecoding" + with sp.else_(): + callback_string.value = "InvalidSN" + + with arg.match("UNKNOWN_TYPE") as a5: + sp.emit(sp.record(_from=_from, sn=sn), tag= "UnknownResponse") + + with arg.match("ERROR") as a5: + self.send_response_message(sp.nat(5), _from, sn, + "Unknown",self.RC_ERR) + with sp.else_(): + callback_string.value = "ErrorInDecoding" + with sp.else_(): + callback_string.value = "UnAuthorized" + + return_value = sp.record(string=sp.some(callback_string.value), prev=prev, + callback_msg=callback_msg) + sp.transfer(return_value, sp.tez(0), callback) + + @sp.entry_point(lazify=False) + def update_handle_btp_error(self, ep): + self.only_owner() + sp.set_entry_point("handle_btp_error", ep) + + @sp.entry_point(lazify=True) + def handle_btp_error(self, svc, sn, code, msg, callback): + """ + BSH handle BTP Error from BMC contract + :param svc: A service name of BSH contract + :param sn: A serial number of a service request + :param code: A response code of a message (RC_OK / RC_ERR) + :param msg: A response message + :param callback: callback function type in bmc_periphery + :return: + """ + + sp.set_type(svc, sp.TString) + sp.set_type(sn, sp.TInt) + sp.set_type(code, sp.TNat) + sp.set_type(msg, sp.TString) + sp.set_type(callback, sp.TContract(sp.TRecord(string=sp.TOption(sp.TString), + svc=sp.TString, sn=sp.TInt, code=sp.TNat, msg=sp.TString))) + + check_caller = self.only_bmc() + handle_btp_error_status = sp.local("handle_btp_error_status", "success") + with sp.if_((svc == self.service_name) & (check_caller == "Authorized") & (sp.len( + self.data.requests.get(sn).from_) != 0)): + emit_msg= sp.concat(["errCode: ", sp.view("string_of_int", self.data.parse_contract, sp.to_int(code), + t=sp.TString).open_some(),", errMsg: ", msg]) + handle_response_serv = self.handle_response_service(sn, self.RC_ERR, emit_msg) + with sp.if_(handle_response_serv != "success"): + handle_btp_error_status.value = handle_response_serv + with sp.else_(): + handle_btp_error_status.value = "UnAuthorized" + + return_value = sp.record(string=sp.some(handle_btp_error_status.value), svc=svc, sn=sn, + code=code, msg=msg) + sp.transfer(return_value, sp.tez(0), callback) + + def handle_response_service(self, sn, code, msg): + """ + :param sn: + :param code: + :param msg: + :return: + """ + sp.set_type(sn, sp.TInt) + sp.set_type(code, sp.TNat) + sp.set_type(msg, sp.TString) + + caller = sp.local("caller", sp.view("str_to_addr", self.data.parse_contract, + self.data.requests.get(sn).from_, + t=sp.TAddress).open_some(), sp.TAddress) + loop = sp.local("loop", sp.len(self.data.requests.get(sn).coin_details), sp.TNat) + response_call_status = sp.local("response_call_status", "success") + check_valid = sp.local("check_valid", True) + + bts_core_address = self.data.bts_core + with sp.if_(loop.value <= self.MAX_BATCH_SIZE): + sp.for item in self.data.requests.get(sn).coin_details: + with sp.if_(check_valid.value == True): + coin_name = item.coin_name + value = item.value + fee = item.fee + amount = value + fee + bts_core_balance = sp.view("balance_of", bts_core_address, + sp.record(owner=caller.value, coin_name=coin_name), t= + sp.TRecord(usable_balance=sp.TNat, locked_balance=sp.TNat, + refundable_balance=sp.TNat, user_balance=sp.TNat) + ).open_some() + # check if caller has enough locked in bts_core + # this case is for transfer fees + with sp.if_((bts_core_balance.locked_balance < amount) & (caller.value != bts_core_address)): + check_valid.value = False + + coin_type = sp.view("coin_type", bts_core_address, coin_name, t=sp.TNat).open_some() + with sp.if_(coin_type == sp.nat(1)): + # finding the balance of bts core to verify if bts core has enough tokens to burn + bts_core_fa2_balance = sp.view("balance_of", bts_core_address, + sp.record(owner=bts_core_address, coin_name=coin_name), t= + sp.TRecord(usable_balance=sp.TNat, locked_balance=sp.TNat, + refundable_balance=sp.TNat, user_balance=sp.TNat) + ).open_some() + # check if bts_core has enough NATIVE_WRAPPED_COIN_TYPE to burn + with sp.if_(bts_core_fa2_balance.user_balance < value): + check_valid.value = False + + with sp.if_(check_valid.value == True): + sp.for _item in self.data.requests.get(sn).coin_details: + # inter score call + handle_response_service_args_type = sp.TRecord( + requester=sp.TAddress, coin_name=sp.TString, value=sp.TNat, fee=sp.TNat, rsp_code=sp.TNat) + handle_response_service_entry_point = sp.contract(handle_response_service_args_type, + self.data.bts_core, "handle_response_service").open_some("invalid call") + handle_response_service_args = sp.record( + requester=caller.value, coin_name=_item.coin_name, + value=_item.value, + fee=_item.fee, rsp_code=code + ) + sp.transfer(handle_response_service_args, sp.tez(0), handle_response_service_entry_point) + + del self.data.requests[sn] + self.data.number_of_pending_requests = sp.as_nat(self.data.number_of_pending_requests-1) + + sp.emit(sp.record(caller=caller.value, sn=sn, code=code, msg=msg), tag="TransferEnd") + with sp.else_(): + response_call_status.value = "Error in bts handle_response_service" + with sp.else_(): + response_call_status.value = "BatchMaxSizeExceed" + + return response_call_status.value + + def _handle_request_service(self, to, assets): + """ + Handle a list of minting/transferring coins/tokens + :param to: An address to receive coins/tokens + :param assets: A list of requested coin respectively with an amount + :return: + """ + sp.set_type(to, sp.TAddress) + sp.set_type(assets, sp.TMap(sp.TNat, types.Types.Asset)) + + status = sp.local("status", "success") + check_validity = sp.local("check_validity", True) + bts_core_fa2_balance = sp.local("fa2_token_balance", sp.nat(0)) + bts_core_address = self.data.bts_core + with sp.if_(sp.len(assets) <= self.MAX_BATCH_SIZE): + parsed_to = to + sp.for i in sp.range(0, sp.len(assets)): + coin_name = assets[i].coin_name + transferred_amount = assets[i].value + valid_coin = sp.view("is_valid_coin", bts_core_address, coin_name, t=sp.TBool).open_some() + with sp.if_(valid_coin == True): + check_transfer = sp.view("check_transfer_restrictions", sp.self_address, sp.record( + coin_name=coin_name, user=parsed_to, value=transferred_amount), t=sp.TBool).open_some() + with sp.if_(check_transfer == True): + native_coin_name, bts_core_balance = sp.match_pair(sp.view("native_coin_balance_of", bts_core_address, + sp.unit, t=sp.TPair(sp.TString, sp.TMutez)).open_some("Invalid view")) + with sp.if_(native_coin_name == coin_name): + with sp.if_(sp.utils.mutez_to_nat(bts_core_balance) < transferred_amount): + check_validity.value = False + with sp.else_(): + coin_type = sp.view("coin_type", bts_core_address, coin_name, t=sp.TNat).open_some() + # check balance_of in case of NON-NATIVE-COIN-TYPE + with sp.if_(coin_type == sp.nat(2)): + coin_address = sp.view("coin_id", bts_core_address, coin_name, t=sp.TAddress).open_some() + bts_core_fa2 = sp.view("get_balance_of", coin_address, + [sp.record(owner=bts_core_address, token_id=sp.nat(0))], + t=sp.TList(t_balance_of_response)).open_some("Invalid view") + sp.for elem in bts_core_fa2: + bts_core_fa2_balance.value = elem.balance + with sp.if_(bts_core_fa2_balance.value < transferred_amount): + check_validity.value = False + + with sp.if_((check_transfer == False) | (valid_coin == False)) : + check_validity.value = False + with sp.if_(check_validity.value == True): + sp.for i in sp.range(0, sp.len(assets)): + # inter score call + mint_args_type = sp.TRecord(to=sp.TAddress, coin_name=sp.TString, value=sp.TNat) + mint_args_type_entry_point = sp.contract(mint_args_type, bts_core_address, "mint").open_some() + mint_args = sp.record(to=parsed_to, coin_name=assets[i].coin_name, value=assets[i].value) + sp.transfer(mint_args, sp.tez(0), mint_args_type_entry_point) + with sp.else_(): + status.value = "UnregisteredCoin" + with sp.else_(): + status.value = "BatchMaxSizeExceed" + + return status.value + + def send_response_message(self, service_type_val, to, sn, msg, code): + """ + + :param service_type_val: value of service_type variant + :param to: + :param sn: + :param msg: + :param code: + :return: + """ + sp.set_type(service_type_val, sp.TNat) + sp.set_type(to, sp.TString) + sp.set_type(sn, sp.TInt) + sp.set_type(msg, sp.TString) + sp.set_type(code, sp.TNat) + + send_message_args_type = sp.TRecord( + to=sp.TString, svc=sp.TString, sn=sp.TInt, msg=sp.TBytes + ) + send_message_entry_point = sp.contract(send_message_args_type, self.data.bmc, "send_message").open_some() + send_message_args = sp.record(to=to, svc=self.service_name, sn=sn, + msg=self.encode_service_message(sp.record(service_type_value=service_type_val, + data=self.encode_response( + sp.record(code=code, message=msg))))) + sp.transfer(send_message_args, sp.tez(0), send_message_entry_point) + + @sp.entry_point(lazify=False) + def update_handle_fee_gathering(self, ep): + self.only_owner() + sp.set_entry_point("handle_fee_gathering", ep) + + @sp.entry_point(lazify=True) + def handle_fee_gathering(self, fa, svc): + """ + BSH handle Gather Fee Message request from BMC contract + :param fa: A BTP address of fee aggregator + :param svc: A name of the service + :return: + """ + sp.set_type(fa, sp.TString) + sp.set_type(svc, sp.TString) + + check_caller = self.only_bmc() + strings.split_btp_address(fa) + with sp.if_((svc == self.service_name) & (check_caller == "Authorized")): + + # call transfer_fees of BTS_Core + transfer_fees_args_type = sp.TString + transfer_fees_entry_point = sp.contract(transfer_fees_args_type, self.data.bts_core, + "transfer_fees").open_some() + sp.transfer(fa, sp.tez(0), transfer_fees_entry_point) + + @sp.onchain_view() + def check_transfer_restrictions(self, params): + """ + + :param params: Record of coin transfer details + :return: + """ + sp.set_type(params, sp.TRecord(coin_name=sp.TString, user=sp.TAddress, value=sp.TNat)) + + with sp.if_((self.data.blacklist.contains(params.user) == False) & + (self.data.token_limit.get(params.coin_name, default_value=sp.nat(0)) >= params.value)): + sp.result(True) + with sp.else_(): + sp.result(False) + + +sp.add_compilation_target("bts_periphery", BTSPeriphery(bmc_address=sp.address("KT1VFtWq2dZDH1rTfLtgMaASMt4UX78omMs2"), + bts_core_address=sp.address("KT1JAippuMfS6Bso8DGmigmTdkgEZUxQxYyX"), + helper_contract=sp.address("KT1HwFJmndBWRn3CLbvhUjdupfEomdykL5a6"), + parse_address=sp.address("KT1Ha8LzZa7ku1F8eytY7hgNKFJ2BKFRqSDh"), + native_coin_name="btp-NetXnHfVqm9iesp.tezos-XTZ", + owner_address = sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP") + )) \ No newline at end of file diff --git a/smartpy/bts/contracts/src/helper.py b/smartpy/bts/contracts/src/helper.py new file mode 100644 index 000000000..94d220c85 --- /dev/null +++ b/smartpy/bts/contracts/src/helper.py @@ -0,0 +1,77 @@ +import smartpy as sp + +Utils = sp.io.import_script_from_url("https://raw.githubusercontent.com/Acurast/acurast-hyperdrive/main/contracts/tezos/libs/utils.py") + + +class Helper(sp.Contract): + def __init__(self): + self.init() + + @sp.onchain_view() + def decode_string(self, params): + sp.set_type(params, sp.TBytes) + decode_string = sp.build_lambda(Utils.RLP.Decoder.decode_string) + sp.result(decode_string(params)) + + @sp.onchain_view() + def prefix_length(self, params): + sp.set_type(params, sp.TBytes) + prefix_length = sp.build_lambda(Utils.RLP.Decoder.prefix_length) + sp.result(prefix_length(params)) + + @sp.onchain_view() + def decode_list(self, params): + sp.set_type(params, sp.TBytes) + decode_list = sp.build_lambda(Utils.RLP.Decoder.decode_list) + sp.result(decode_list(params)) + + @sp.onchain_view() + def is_list(self, params): + sp.set_type(params, sp.TBytes) + is_list = sp.build_lambda(Utils.RLP.Decoder.is_list) + sp.result(is_list(params)) + + @sp.onchain_view() + def of_string(self, params): + sp.set_type(params, sp.TString) + encode_string_packed = sp.build_lambda(Utils.Bytes.of_string) + sp.result(encode_string_packed(params)) + + @sp.onchain_view() + def encode_string(self, params): + sp.set_type(params, sp.TString) + encode_string_packed = sp.build_lambda(Utils.RLP.Encoder.encode_string) + sp.result(encode_string_packed(params)) + + @sp.onchain_view() + def encode_nat(self, params): + sp.set_type(params, sp.TNat) + encode_nat_packed = sp.build_lambda(Utils.RLP.Encoder.encode_nat) + sp.result(encode_nat_packed(params)) + + @sp.onchain_view() + def of_nat(self, params): + sp.set_type(params, sp.TNat) + encode_nat_packed = sp.build_lambda(Utils.Bytes.of_nat) + sp.result(encode_nat_packed(params)) + + @sp.onchain_view() + def with_length_prefix(self, params): + sp.set_type(params, sp.TBytes) + encode_length_packed = sp.build_lambda(Utils.RLP.Encoder.with_length_prefix) + sp.result(encode_length_packed(params)) + + @sp.onchain_view() + def without_length_prefix(self, params): + sp.set_type(params, sp.TBytes) + decode = sp.build_lambda(Utils.RLP.Decoder.without_length_prefix) + sp.result(decode(params)) + + @sp.onchain_view() + def encode_list(self, params): + sp.set_type(params, sp.TList(sp.TBytes)) + encode_list_packed = sp.build_lambda(Utils.RLP.Encoder.encode_list) + sp.result(encode_list_packed(params)) + + +sp.add_compilation_target("helper", Helper()) \ No newline at end of file diff --git a/smartpy/bts/contracts/src/parse_address.py b/smartpy/bts/contracts/src/parse_address.py new file mode 100644 index 000000000..2410253a9 --- /dev/null +++ b/smartpy/bts/contracts/src/parse_address.py @@ -0,0 +1,192 @@ +import smartpy as sp +Utils = sp.io.import_script_from_url("https://raw.githubusercontent.com/RomarQ/tezos-sc-utils/main/smartpy/utils.py") + + +class ParseAddress(sp.Contract): + tz_prefixes = sp.map({ + sp.bytes('0x0000'): sp.string('tz1'), + sp.bytes('0x0001'): sp.string('tz2'), + sp.bytes('0x0002'): sp.string('tz3'), + sp.bytes('0x0003'): sp.string('tz4') + }) + base58_encodings = sp.list([ + sp.map({"prefix": "tz1", "elem1": "6", "elem2": "161", "elem3": "159", "len": "20"}), + sp.map({"prefix": "tz2", "elem1": "6", "elem2": "161", "elem3": "161", "len": "20"}), + sp.map({"prefix": "tz3", "elem1": "6", "elem2": "161", "elem3": "164", "len": "20"}), + sp.map({"prefix": "tz4", "elem1": "6", "elem2": "161", "elem3": "16", "len": "20"}), + sp.map({"prefix": "KT1", "elem1": "2", "elem2": "90", "elem3": "121", "len": "20"}), + ]) + + def __init__(self): + self.init() + + def unforge_address(self, data): + """Decode address or key_hash from bytes. + + :param data: encoded address or key_hash + :returns: base58 encoded address + """ + sp.set_type(data, sp.TBytes) + byt = sp.slice(data, 6, 22).open_some() + prefix = sp.slice(byt, 0, 2).open_some() + starts_with = sp.slice(byt, 0, 1).open_some() + ends_with = sp.slice(byt, 21, 1).open_some() + sliced_byte = sp.slice(byt, 1, 20).open_some() + local_byte = sp.local("local_byte", sp.bytes("0x")) + return_value = sp.local("return_value", "tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg", sp.TString) + + sp.for item in self.tz_prefixes.items(): + sp.if item.key == prefix: + return_value.value = self.base58_encode(sp.slice(byt, 2, 20).open_some(), Utils.Bytes.of_string(item.value), local_byte) + + sp.if (starts_with == sp.bytes("0x01")) & (ends_with == sp.bytes("0x00")): + return_value.value = self.base58_encode(sliced_byte, Utils.Bytes.of_string("KT1"), local_byte) + sp.if (starts_with == sp.bytes("0x02")) & (ends_with == sp.bytes("0x00")): + return_value.value = self.base58_encode(sliced_byte, Utils.Bytes.of_string("txr1"), local_byte) + sp.if (starts_with == sp.bytes("0x03")) & (ends_with == sp.bytes("0x00")): + return_value.value = self.base58_encode(sliced_byte, Utils.Bytes.of_string("sr1"), local_byte) + + return return_value.value + + def tb(self, _list): + byte_str = sp.local("byte_str", sp.bytes("0x")) + sp.for num in _list: + byte_str.value += Utils.Bytes.of_nat(num) + return byte_str.value + + def base58_encode(self, byt_array, prefix, _byte): + """ + Encode data using Base58 with checksum and add an according binary prefix in the end. + :param byt_array: Array of bytes + :param prefix: Human-readable prefix (use b'') e.g. b'tz', b'KT', etc + :param local_byte: local variable + + :returns: bytes (use string.decode()) + """ + length_v = sp.to_int(sp.len(byt_array)) + encoding = sp.local("encode", sp.map({})) + byte_from_tbl = sp.local("byte_from_tbl", sp.bytes("0x")) + byte_value = _byte + + sp.for enc in self.base58_encodings: + sp.if (length_v == Utils.Int.of_string(enc["len"])) & (prefix == Utils.Bytes.of_string(enc["prefix"])): + encoding.value = enc + byte_from_tbl.value = self.tb([sp.as_nat(Utils.Int.of_string(enc["elem1"])), + sp.as_nat(Utils.Int.of_string(enc["elem2"])), + sp.as_nat(Utils.Int.of_string(enc["elem3"]))]) + sha256_encoding = sp.sha256(sp.sha256(byte_from_tbl.value + byt_array)) + sha256_encoding = byte_from_tbl.value + byt_array + sp.slice(sha256_encoding, 0, 4).open_some() + acc = sp.local("for_while_loop", Utils.Int.of_bytes(sha256_encoding)) + alphabet = Utils.Bytes.of_string("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + base = 58 + sp.while acc.value > 0: + (acc.value, idx) = sp.match_pair(sp.ediv(acc.value, base).open_some()) + byte_value.value = sp.slice(alphabet, idx, 1).open_some() + byte_value.value + + return sp.unpack(sp.bytes("0x050100000024") + byte_value.value, sp.TString).open_some() + + @sp.onchain_view() + def add_to_str(self, params): + sp.set_type(params, sp.TAddress) + sp.result(self.unforge_address(sp.pack(params))) + + def _to_addr(self, params): + string_in_bytes = sp.pack(params) + actual_prefix = sp.local("actual_prefix", "") + addr = sp.local("addr", sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg")) + alphabet = Utils.Bytes.of_string("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + sp.if sp.len(string_in_bytes) == sp.nat(42): + string_in_bytes = sp.slice(string_in_bytes, 6, 36).open_some() + element_list = sp.range(0, sp.len(alphabet), 1) + temp_map = sp.local("temp_map", {}) + temp_var = sp.local("y", 0) + sp.for elem in element_list: + temp_var.value = elem + temp_map.value[sp.slice(alphabet, temp_var.value, 1).open_some()] = temp_var.value + decimal = sp.local("decimal", 0) + base = sp.len(alphabet) + element_list_2 = sp.range(0, sp.len(string_in_bytes), 1) + + sp.for elem in element_list_2: + decimal.value = decimal.value * base + temp_map.value[sp.slice(string_in_bytes, elem, 1).open_some()] + byt_value = Utils.Bytes.of_nat(sp.as_nat(sp.to_int(decimal.value))) + new_byt_value = sp.slice(byt_value, 0, sp.as_nat(sp.len(byt_value) - 4)).open_some() + prefix = sp.slice(new_byt_value, 0, 3).open_some() + prefix_len = sp.range(0, sp.len(prefix), 1) + temp_var3 = sp.local("z", 0) + list_string = sp.local("list_string", []) + sp.for x in prefix_len: + temp_var3.value = x + list_string.value.push(Utils.Int.of_bytes(sp.slice(prefix, temp_var3.value, 1).open_some())) + value = sp.slice(new_byt_value, 3, sp.as_nat(sp.len(new_byt_value) - 3)) + byte_local = sp.local("byt_old", sp.bytes("0x")) + + sp.for enc in self.base58_encodings: + byte_local.value = self.tb([sp.as_nat(Utils.Int.of_string(enc["elem1"])), + sp.as_nat(Utils.Int.of_string(enc["elem2"])), + sp.as_nat(Utils.Int.of_string(enc["elem3"]))]) + sp.if byte_local.value == prefix: + actual_prefix.value = enc["prefix"] + + sp.for item in self.tz_prefixes.items(): + sp.if item.value == actual_prefix.value: + decoded_address = sp.unpack(sp.bytes("0x050a00000016") + item.key + value.open_some(), + sp.TAddress) + addr.value = decoded_address.open_some() + sp.if actual_prefix.value == "KT1": + decoded_address = sp.unpack( + sp.bytes("0x050a00000016") + sp.bytes("0x01") + value.open_some() + sp.bytes("0x00"), + sp.TAddress) + addr.value = decoded_address.open_some() + sp.if actual_prefix.value == "txr1": + decoded_address = sp.unpack( + sp.bytes("0x050a00000016") + sp.bytes("0x02") + value.open_some() + sp.bytes("0x00"), + sp.TAddress) + addr.value = decoded_address.open_some() + sp.if actual_prefix.value == "sr1": + decoded_address = sp.unpack( + sp.bytes("0x050a00000016") + sp.bytes("0x03") + value.open_some() + sp.bytes("0x00"), + sp.TAddress) + addr.value = decoded_address.open_some() + + sp.if actual_prefix.value == "": + addr.value = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg") + with sp.else_(): + addr.value = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg") + + return addr.value + + @sp.onchain_view() + def str_to_addr(self, params): + sp.set_type(params, sp.TString) + sp.result(self._to_addr(params)) + + @sp.onchain_view() + def string_of_int(self, params): + sp.set_type(params, sp.TInt) + sp.result(Utils.String.of_int(params)) + + +@sp.add_test(name="Conversion") +def test(): + alice=sp.test_account("Alice") + c1 = ParseAddress() + scenario = sp.test_scenario() + scenario.h1("Conversion") + scenario += c1 + c1.add_to_str(sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP")) + scenario.verify(c1.add_to_str(sp.address("KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr")) == "KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr") + scenario.verify(c1.add_to_str(sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP")) == "tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP") + + c1.str_to_addr("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP") + c1.str_to_addr("KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr") + scenario.verify(c1.str_to_addr("KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr") == sp.address("KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr")) + scenario.verify(c1.str_to_addr("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP") == sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP")) + # invalid address + scenario.verify(c1.str_to_addr("tz1g3pJZPifxhN") == sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg")) + + + +sp.add_compilation_target("parse_address", ParseAddress()) + + diff --git a/smartpy/bts/contracts/tests/bts_periphery_test.py b/smartpy/bts/contracts/tests/bts_periphery_test.py new file mode 100644 index 000000000..0b1524714 --- /dev/null +++ b/smartpy/bts/contracts/tests/bts_periphery_test.py @@ -0,0 +1,125 @@ +import smartpy as sp + +BTSPeriphery = sp.io.import_script_from_url("file:./contracts/src/bts_periphery.py") +BTSCore = sp.io.import_script_from_url("file:./contracts/src/bts_core.py") +BTSOwnerManager = sp.io.import_script_from_url("file:./contracts/src/bts_owner_manager.py") +ParseAddress = sp.io.import_script_from_url("file:./contracts/src/parse_address.py") +Helper = sp.io.import_script_from_url("file:./contracts/src/helper.py") + + +@sp.add_test("BTSPeripheryTest") +def test(): + sc = sp.test_scenario() + + # test account + admin = sp.test_account('admin') + + def deploy_bts_periphery_contract(core_address, helper, parse): + bmc = sp.test_account("bmc") + _bts_periphery_contract = BTSPeriphery.BTSPeriphery( + bmc_address=bmc.address, bts_core_address=core_address, helper_contract=helper, parse_address=parse, + native_coin_name='NativeCoin', owner_address=admin.address) + return _bts_periphery_contract + + def deploy_parse_contract(): + _bts_parse_contract = ParseAddress.ParseAddress() + return _bts_parse_contract + + def deploy_helper(): + _bts_helper = Helper.Helper() + return _bts_helper + + def deploy_bts_core_contract(_bts_owner_manager_contract): + _bts_core_contract = BTSCore.BTSCore( + owner_manager=_bts_owner_manager_contract, + _native_coin_name="Tok1", + _fee_numerator=sp.nat(1000), + _fixed_fee=sp.nat(10)) + return _bts_core_contract + + def deploy_bts_owner_manager_contract(): + _bts_owner_manager_contract = BTSOwnerManager.BTSOwnerManager(admin.address) + return _bts_owner_manager_contract + + bts_owner_manager_contract = deploy_bts_owner_manager_contract() + sc += bts_owner_manager_contract + + bts_core_contract = deploy_bts_core_contract(bts_owner_manager_contract.address) + sc += bts_core_contract + + bts_helper_contract = deploy_helper() + sc += bts_helper_contract + + bts_parse_contract = deploy_parse_contract() + sc += bts_parse_contract + + # deploy bts_periphery contract + bts_periphery_contract = deploy_bts_periphery_contract(bts_core_contract.address, bts_helper_contract.address, + bts_parse_contract.address) + sc += bts_periphery_contract + + bts_core_contract.update_bts_periphery(bts_periphery_contract.address).run(sender=admin.address) + + # Scenario 1: set token limit + + # Test cases: + # 1: set_token_limit from non-bts_periphery contract + bts_periphery_contract.set_token_limit(sp.map({"Tok2": sp.nat(5), "BB": sp.nat(2)})).run(sender=admin.address, + valid=False, + exception='Unauthorized') + + # 2: set token limit for Tok2 coin to 5 and BB coin to 2 from bts_periphery_contract + bts_periphery_contract.set_token_limit(sp.map({"Tok2": sp.nat(5), "BB": sp.nat(2)})).run( + sender=bts_core_contract.address) + + # 3: verifying the value of token limit + sc.verify(bts_periphery_contract.data.token_limit["Tok2"] == sp.nat(5)) + + # 4: modify already set data + bts_periphery_contract.set_token_limit(sp.map({"Tok2": sp.nat(15), "BB": sp.nat(22)})).run( + sender=bts_core_contract.address) + + # 5: verifying the value of token limit after change + sc.verify(bts_periphery_contract.data.token_limit["BB"] == sp.nat(22)) + + # Scenario 2: send service message + + # Test cases: + # 1 : send_service_message called by bts-core + bts_periphery_contract.send_service_message( + sp.record( + _from=sp.address("tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW"), + to="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW", + coin_details=[ + sp.record(coin_name="Tok1", value=sp.nat(10), fee=sp.nat(2)) + ] + )).run(sender=bts_core_contract.address).run(sender=bts_core_contract.address) + + # 2 : send_service_message called by non-bts-core + bts_periphery_contract.send_service_message( + sp.record( + _from=sp.address("tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW"), + to="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW", + coin_details=[ + sp.record(coin_name="Tok1", value=sp.nat(10), fee=sp.nat(2)) + ] + )).run(sender=admin, valid=False, exception='Unauthorized') + + # 3: verify if request message is correct + sc.show(bts_periphery_contract.data.requests[1]) + sc.verify_equal( + bts_periphery_contract.data.requests[1], + sp.record( + coin_details=[ + sp.record( + coin_name='Tok1', + value=10, + fee=2 + ) + ], + from_='tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW', + to='btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW')) + + # 4: verify request data + sc.verify(bts_periphery_contract.data.number_of_pending_requests == 1) + sc.verify(bts_periphery_contract.data.serial_no == 1) diff --git a/smartpy/bts/contracts/tests/btscore_test.py b/smartpy/bts/contracts/tests/btscore_test.py new file mode 100644 index 000000000..9df53bea4 --- /dev/null +++ b/smartpy/bts/contracts/tests/btscore_test.py @@ -0,0 +1,225 @@ +import smartpy as sp + +BTSCore = sp.io.import_script_from_url("file:./contracts/src/bts_core.py") +BTSOwnerManager = sp.io.import_script_from_url("file:./contracts/src/bts_owner_manager.py") +BTSPeriphery = sp.io.import_script_from_url("file:./contracts/src/bts_periphery.py") +BMCHelper = sp.io.import_script_from_url("file:./contracts/src/helper.py") +ParseAddress = sp.io.import_script_from_url("file:./contracts/src/parse_address.py") + + +@sp.add_test("BTSCoreTest") +def test(): + sc = sp.test_scenario() + sc.h1("BTSCore") + + # test account + bob = sp.test_account("Bob") + owner = sp.test_account("owner") + + # deploy BTSCore contract + bts_owner_manager = deploy_bts_owner_manager_contract(owner.address) + sc += bts_owner_manager + bts_core_contract = deploy_bts_core_contract(bts_owner_manager.address) + sc += bts_core_contract + + helper_contract = deploy_helper_contract() + sc += helper_contract + + parse_address = deploy_parse_address() + sc += parse_address + + bts_periphery = deploy_bts_periphery_contract(bts_core_contract.address, helper_contract.address, + parse_address.address, owner.address) + sc += bts_periphery + fa2 = deploy_fa2_contract(bts_periphery.address) + sc += fa2 + + # Scenario 1: BTSCore Scenario + + # Test cases: + # 1: verify owner manager + sc.verify_equal(bts_core_contract.data.bts_owner_manager, bts_owner_manager.address) + + # 2: verify update_bts_periphery function + bts_core_contract.update_bts_periphery(bts_periphery.address).run(sender=owner.address) + sc.verify_equal(bts_core_contract.data.bts_periphery_address, sp.some(bts_periphery.address)) + + # Scenario 2: set_fee_ratio + + # Test cases: + # 1: token doesn't exist + bts_core_contract.set_fee_ratio(name=sp.string("ABC"), fee_numerator=sp.nat(100), fixed_fee=sp.nat(10)).run( + sender=owner.address, valid=False, exception='TokenNotExists') + + # 2: fee numerator is greater than denominator + bts_core_contract.set_fee_ratio(name=sp.string("BTSCOIN"), fee_numerator=sp.nat(10000), fixed_fee=sp.nat(10)).run( + sender=owner.address, valid=False, exception='InvalidSetting') + + # 3: fixed fee is 0 + bts_core_contract.set_fee_ratio(name=sp.string("BTSCOIN"), fee_numerator=sp.nat(100), fixed_fee=sp.nat(0)).run( + sender=owner.address, valid=False, exception='LessThan0') + + # 4: valid condition + bts_core_contract.set_fee_ratio(name=sp.string("BTSCOIN"), fee_numerator=sp.nat(100), fixed_fee=sp.nat(10)).run( + sender=owner.address) + + # 5: set_fee_ratio called by non-owner + bts_core_contract.set_fee_ratio(name=sp.string("BTSCOIN"), fee_numerator=sp.nat(100), fixed_fee=sp.nat(10)).run( + sender=bob.address, valid=False, exception='Unauthorized') + + # 5: verify fees + sc.verify_equal(bts_core_contract.data.coin_details["BTSCOIN"].fee_numerator, 100) + sc.verify_equal(bts_core_contract.data.coin_details["BTSCOIN"].fixed_fee, 10) + + # Scenario 2: register + + # Test cases: + # 1: native coin (native_coin = BTSCOIN) + bts_core_contract.register( + name=sp.string("BTSCOIN"), + fee_numerator=sp.nat(10), + fixed_fee=sp.nat(2), + addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiMEc"), + token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")}) + ).run(sender=owner.address, valid=False, exception='ExistNativeCoin') + + # 2: fee numerator is greater than denominator + bts_core_contract.register( + name=sp.string("new_coin1"), + fee_numerator=sp.nat(100000), + fixed_fee=sp.nat(2), + addr=fa2.address, + token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")}) + ).run(sender=owner.address, valid=False, exception='InvalidSetting') + + # 3: valid case + bts_core_contract.register( + name=sp.string("new_coin"), + fee_numerator=sp.nat(10), + fixed_fee=sp.nat(2), + addr=fa2.address, + token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")}) + ).run(sender=owner.address) + + # 4: verify the registered value + sc.verify_equal(bts_core_contract.data.coins_name, ['new_coin', 'BTSCOIN']) + sc.verify_equal(bts_core_contract.data.coin_details['new_coin'], + sp.record(addr=fa2.address, coin_type=2, fee_numerator=10, fixed_fee=2)) + + # 5: existing coin name + bts_core_contract.register( + name=sp.string("new_coin"), + fee_numerator=sp.nat(10), + fixed_fee=sp.nat(2), + addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiMEc"), + token_metadata=sp.map({"ff": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"ff": sp.bytes("0x0dae11")}) + ).run(sender=owner.address, valid=False, exception="ExistCoin") + + # 6: registered NON-NATIVE COIN_TYPE by non-owner + bts_core_contract.register( + name=sp.string("new_coin"), + fee_numerator=sp.nat(10), + fixed_fee=sp.nat(2), + addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiMEc"), + token_metadata=sp.map({"ff": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"ff": sp.bytes("0x0dae11")}) + ).run(sender=bob.address, valid=False, exception="Unauthorized") + + # 7: register NON-NATIVE COIN_TYPE + bts_core_contract.register( + name=sp.string("new_coin2"), + fee_numerator=sp.nat(10), + fixed_fee=sp.nat(2), + addr=fa2.address, + token_metadata=sp.map({"ff": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"ff": sp.bytes("0x0dae11")}) + ).run(sender=owner.address, valid=False, exception="AddressExists") + + # 8: register NATIVE WRAPPED COIN_TYPE + bts_core_contract.register( + name=sp.string("wrapped-coin"), + fee_numerator=sp.nat(10), + fixed_fee=sp.nat(2), + addr=sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg"), + token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}), + metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")}) + ).run(sender=owner.address) + + # Scenario 3: contract getters + + # Test cases: + # 1: verify coin_id functio + sc.verify_equal(bts_core_contract.coin_id('new_coin'), fa2.address) + + # 2: verify is_valid_coin function + sc.verify_equal(bts_core_contract.is_valid_coin('new_coin'), True) + sc.verify_equal(bts_core_contract.is_valid_coin('not_valid'), False) + + # 3: verify fee_ratio function + sc.verify_equal(bts_core_contract.fee_ratio('new_coin'), sp.record(fee_numerator=10, fixed_fee=2)) + + # 4: verify balance_of function + sc.verify_equal( + bts_core_contract.balance_of( + sp.record(owner=owner.address, coin_name='new_coin') + ), + sp.record(usable_balance=0, locked_balance=0, refundable_balance=0, user_balance=0) + ) + + # 5: verify balance_of_batch function + bts_core_contract.balance_of_batch( + sp.record(owner=owner.address, coin_names=['new_coin', 'BTSCOIN']) + ) + sc.verify_equal( + bts_core_contract.balance_of_batch( + sp.record(owner=owner.address, coin_names=['new_coin', 'BTSCOIN']) + ), + [ + sp.record(locked_balance=0, refundable_balance=0, usable_balance=0, user_balance=0), + sp.record(locked_balance=0, refundable_balance=0, usable_balance=0, user_balance=0) + ] + ) + + +def deploy_bts_core_contract(bts_owner_manager_contract): + bts_core_contract = BTSCore.BTSCore( + owner_manager=bts_owner_manager_contract, + _native_coin_name="BTSCOIN", + _fee_numerator=sp.nat(1000), + _fixed_fee=sp.nat(10) + ) + return bts_core_contract + + +def deploy_bts_owner_manager_contract(owner): + bts_owner_manager_contract = BTSOwnerManager.BTSOwnerManager(owner) + return bts_owner_manager_contract + + +def deploy_bts_periphery_contract(core_address, helper, parse, owner): + bmc = sp.test_account("bmc") + bts_periphery_contract = BTSPeriphery.BTSPeriphery( + bmc_address=bmc.address, bts_core_address=core_address, helper_contract=helper, parse_address=parse, + native_coin_name='BTSCoin', owner_address=owner) + return bts_periphery_contract + + +def deploy_fa2_contract(admin_address): + fa2_contract = BTSCore.FA2_contract.SingleAssetToken(admin=admin_address, + metadata=sp.big_map({"ss": sp.bytes("0x0dae11")}), + token_metadata=sp.map({"ff": sp.bytes("0x0dae11")})) + return fa2_contract + + +def deploy_helper_contract(): + helper_contract = BMCHelper.Helper() + return helper_contract + + +def deploy_parse_address(): + parse_address = ParseAddress.ParseAddress() + return parse_address diff --git a/smartpy/bts/package-lock.json b/smartpy/bts/package-lock.json new file mode 100644 index 000000000..2bec67e40 --- /dev/null +++ b/smartpy/bts/package-lock.json @@ -0,0 +1,1431 @@ +{ + "name": "create-tezos-app", + "version": "1.0.3", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "create-tezos-app", + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "@taquito/signer": "^14.0.0", + "@taquito/taquito": "^14.0.0", + "dotenv": "^16.0.3", + "ts-node": "^10.9.1" + }, + "bin": { + "create-tezos-app": "bin/cli.js" + }, + "devDependencies": { + "typescript": "^4.8.4" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "dependencies": { + "@stablelib/int": "^1.0.1" + } + }, + "node_modules/@stablelib/blake2b": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/blake2b/-/blake2b-1.0.1.tgz", + "integrity": "sha512-B3KyKoBAjkIFeH7romcF96i+pVFYk7K2SBQ1pZvaxV+epSBXJ+n0C66esUhyz6FF+5FbdQVm77C5fzGFcEZpKA==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/bytes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==" + }, + "node_modules/@stablelib/constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz", + "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==" + }, + "node_modules/@stablelib/ed25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", + "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "dependencies": { + "@stablelib/random": "^1.0.2", + "@stablelib/sha512": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==" + }, + "node_modules/@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==" + }, + "node_modules/@stablelib/keyagreement": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", + "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "dependencies": { + "@stablelib/bytes": "^1.0.1" + } + }, + "node_modules/@stablelib/nacl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@stablelib/nacl/-/nacl-1.0.4.tgz", + "integrity": "sha512-PJ2U/MrkXSKUM8C4qFs87WeCNxri7KQwR8Cdwm9q2sweGuAtTvOJGuW0F3N+zn+ySLPJA98SYWSSpogMJ1gCmw==", + "dependencies": { + "@stablelib/poly1305": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1", + "@stablelib/x25519": "^1.0.3", + "@stablelib/xsalsa20": "^1.0.2" + } + }, + "node_modules/@stablelib/poly1305": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz", + "integrity": "sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==", + "dependencies": { + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/salsa20": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/salsa20/-/salsa20-1.0.2.tgz", + "integrity": "sha512-nfjKzw0KTKrrKBasEP+j7UP4I8Xudom8lVZIBCp0kQNARXq72IlSic0oabg2FC1NU68L4RdHrNJDd8bFwrphYA==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/sha512": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", + "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==" + }, + "node_modules/@stablelib/x25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", + "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "dependencies": { + "@stablelib/keyagreement": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/xsalsa20": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/xsalsa20/-/xsalsa20-1.0.2.tgz", + "integrity": "sha512-7XdBGbcNgBShmuhDXv1G1WPVCkjZdkb1oPMzSidO7Fve0MHntH6TjFkj5bfLI+aRE+61weO076vYpP/jmaAYog==", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/salsa20": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@taquito/http-utils": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-14.0.0.tgz", + "integrity": "sha512-ZWZzod/+/OEE26b9CnDRjHGfUKBJft3aXv/e/A9bTHAtvRNJqGIhofHcDg/jTaolBMarCF2b3XBYw35aOOSk4A==", + "dependencies": { + "axios": "^0.26.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/local-forging": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-14.0.0.tgz", + "integrity": "sha512-Nm0xGmS1Jzd+tU0a/8Y8XuTghbiPBgHDLo+e4141TK3OAwTzOw0an+w3xK9QVfzvxfIcZBSMjeMZzOwDdiqkJQ==", + "dependencies": { + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/michel-codec": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-14.0.0.tgz", + "integrity": "sha512-ftnBvUVddlHBqvQbGPHEb26KrS4lIcaZ1eIpYJWiz+akb4Pcfyq7j/OEsDZbB7Pl2FP9hqu7ZygOF34zY6Lrtw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/michelson-encoder": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-14.0.0.tgz", + "integrity": "sha512-KIS+xl4rKfnd6hf9LUr6W+Pb7gv8F/Qsx0fho9CtM2PodKvdef3YlvkpScBUM9QZntAlvq2XQXUVXcZkbvxygw==", + "dependencies": { + "@taquito/rpc": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2", + "fast-json-stable-stringify": "^2.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/rpc": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-14.0.0.tgz", + "integrity": "sha512-FMfb80sA+VJwNx8OTNN07boDAt2roISqLLCUgmOIwy/cFDqhII7gdS4aYWJWqlKbdPKCPqh3a3ZQD1X/jyQHOA==", + "dependencies": { + "@taquito/http-utils": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/signer": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-14.0.0.tgz", + "integrity": "sha512-vDqp/quzAsOiVikUt5MYUKhHI3S9qlasazyXs99xK9qpGLotbx6aseKcfb/dkaTo4/eoMzP4XzTVdnk0AqcCkw==", + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.2", + "@stablelib/nacl": "^1.0.3", + "@taquito/taquito": "^14.0.0", + "@taquito/utils": "^14.0.0", + "elliptic": "^6.5.4", + "pbkdf2": "^3.1.2", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/taquito": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-14.0.0.tgz", + "integrity": "sha512-JaXfvqOCF3dkwXxhe00Ravb8WLMC2VqJxerf4GrGUEpJw+NAkwbGEb8k/52+MQQdlxMPepZdPPru/HM3nFG0Tw==", + "hasInstallScript": true, + "dependencies": { + "@taquito/http-utils": "^14.0.0", + "@taquito/local-forging": "^14.0.0", + "@taquito/michel-codec": "^14.0.0", + "@taquito/michelson-encoder": "^14.0.0", + "@taquito/rpc": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2", + "rxjs": "^6.6.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@taquito/utils": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-14.0.0.tgz", + "integrity": "sha512-0RSHn/CzbcbMdldJJIlXyxOvAajwmL6iPKJ6NaRyYJqqLM2CxYjG72KpXVv716pCMV1MbIWsOAr9FKbxW73PsA==", + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.2", + "@types/bs58check": "^2.1.0", + "bignumber.js": "^9.0.2", + "blakejs": "^1.2.1", + "bs58check": "^2.1.2", + "buffer": "^6.0.3", + "elliptic": "^6.5.4", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, + "node_modules/@types/bs58check": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.0.tgz", + "integrity": "sha512-OxsysnJQh82vy9DRbOcw9m2j/WiyqZLn0YBhKxdQ+aCwoHj+tWzyCgpwAkr79IfDXZKxc6h7k89T9pwS78CqTQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + }, + "node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz", + "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==", + "engines": { + "node": "*" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/typedarray-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz", + "integrity": "sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "requires": { + "@stablelib/int": "^1.0.1" + } + }, + "@stablelib/blake2b": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/blake2b/-/blake2b-1.0.1.tgz", + "integrity": "sha512-B3KyKoBAjkIFeH7romcF96i+pVFYk7K2SBQ1pZvaxV+epSBXJ+n0C66esUhyz6FF+5FbdQVm77C5fzGFcEZpKA==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/bytes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==" + }, + "@stablelib/constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz", + "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==" + }, + "@stablelib/ed25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", + "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "requires": { + "@stablelib/random": "^1.0.2", + "@stablelib/sha512": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==" + }, + "@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==" + }, + "@stablelib/keyagreement": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", + "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "requires": { + "@stablelib/bytes": "^1.0.1" + } + }, + "@stablelib/nacl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@stablelib/nacl/-/nacl-1.0.4.tgz", + "integrity": "sha512-PJ2U/MrkXSKUM8C4qFs87WeCNxri7KQwR8Cdwm9q2sweGuAtTvOJGuW0F3N+zn+ySLPJA98SYWSSpogMJ1gCmw==", + "requires": { + "@stablelib/poly1305": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1", + "@stablelib/x25519": "^1.0.3", + "@stablelib/xsalsa20": "^1.0.2" + } + }, + "@stablelib/poly1305": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz", + "integrity": "sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==", + "requires": { + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/salsa20": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/salsa20/-/salsa20-1.0.2.tgz", + "integrity": "sha512-nfjKzw0KTKrrKBasEP+j7UP4I8Xudom8lVZIBCp0kQNARXq72IlSic0oabg2FC1NU68L4RdHrNJDd8bFwrphYA==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/sha512": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", + "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==" + }, + "@stablelib/x25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", + "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "requires": { + "@stablelib/keyagreement": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "@stablelib/xsalsa20": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/xsalsa20/-/xsalsa20-1.0.2.tgz", + "integrity": "sha512-7XdBGbcNgBShmuhDXv1G1WPVCkjZdkb1oPMzSidO7Fve0MHntH6TjFkj5bfLI+aRE+61weO076vYpP/jmaAYog==", + "requires": { + "@stablelib/binary": "^1.0.1", + "@stablelib/salsa20": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "@taquito/http-utils": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-14.0.0.tgz", + "integrity": "sha512-ZWZzod/+/OEE26b9CnDRjHGfUKBJft3aXv/e/A9bTHAtvRNJqGIhofHcDg/jTaolBMarCF2b3XBYw35aOOSk4A==", + "requires": { + "axios": "^0.26.0" + } + }, + "@taquito/local-forging": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-14.0.0.tgz", + "integrity": "sha512-Nm0xGmS1Jzd+tU0a/8Y8XuTghbiPBgHDLo+e4141TK3OAwTzOw0an+w3xK9QVfzvxfIcZBSMjeMZzOwDdiqkJQ==", + "requires": { + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2" + } + }, + "@taquito/michel-codec": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-14.0.0.tgz", + "integrity": "sha512-ftnBvUVddlHBqvQbGPHEb26KrS4lIcaZ1eIpYJWiz+akb4Pcfyq7j/OEsDZbB7Pl2FP9hqu7ZygOF34zY6Lrtw==" + }, + "@taquito/michelson-encoder": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-14.0.0.tgz", + "integrity": "sha512-KIS+xl4rKfnd6hf9LUr6W+Pb7gv8F/Qsx0fho9CtM2PodKvdef3YlvkpScBUM9QZntAlvq2XQXUVXcZkbvxygw==", + "requires": { + "@taquito/rpc": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2", + "fast-json-stable-stringify": "^2.1.0" + } + }, + "@taquito/rpc": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-14.0.0.tgz", + "integrity": "sha512-FMfb80sA+VJwNx8OTNN07boDAt2roISqLLCUgmOIwy/cFDqhII7gdS4aYWJWqlKbdPKCPqh3a3ZQD1X/jyQHOA==", + "requires": { + "@taquito/http-utils": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2" + } + }, + "@taquito/signer": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-14.0.0.tgz", + "integrity": "sha512-vDqp/quzAsOiVikUt5MYUKhHI3S9qlasazyXs99xK9qpGLotbx6aseKcfb/dkaTo4/eoMzP4XzTVdnk0AqcCkw==", + "requires": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.2", + "@stablelib/nacl": "^1.0.3", + "@taquito/taquito": "^14.0.0", + "@taquito/utils": "^14.0.0", + "elliptic": "^6.5.4", + "pbkdf2": "^3.1.2", + "typedarray-to-buffer": "^4.0.0" + } + }, + "@taquito/taquito": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-14.0.0.tgz", + "integrity": "sha512-JaXfvqOCF3dkwXxhe00Ravb8WLMC2VqJxerf4GrGUEpJw+NAkwbGEb8k/52+MQQdlxMPepZdPPru/HM3nFG0Tw==", + "requires": { + "@taquito/http-utils": "^14.0.0", + "@taquito/local-forging": "^14.0.0", + "@taquito/michel-codec": "^14.0.0", + "@taquito/michelson-encoder": "^14.0.0", + "@taquito/rpc": "^14.0.0", + "@taquito/utils": "^14.0.0", + "bignumber.js": "^9.0.2", + "rxjs": "^6.6.3" + } + }, + "@taquito/utils": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-14.0.0.tgz", + "integrity": "sha512-0RSHn/CzbcbMdldJJIlXyxOvAajwmL6iPKJ6NaRyYJqqLM2CxYjG72KpXVv716pCMV1MbIWsOAr9FKbxW73PsA==", + "requires": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.2", + "@types/bs58check": "^2.1.0", + "bignumber.js": "^9.0.2", + "blakejs": "^1.2.1", + "bs58check": "^2.1.2", + "buffer": "^6.0.3", + "elliptic": "^6.5.4", + "typedarray-to-buffer": "^4.0.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, + "@types/bs58check": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.0.tgz", + "integrity": "sha512-OxsysnJQh82vy9DRbOcw9m2j/WiyqZLn0YBhKxdQ+aCwoHj+tWzyCgpwAkr79IfDXZKxc6h7k89T9pwS78CqTQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + }, + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, + "base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bignumber.js": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz", + "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==" + }, + "blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "typedarray-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz", + "integrity": "sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ==" + }, + "typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + } + } +} diff --git a/smartpy/bts/package.json b/smartpy/bts/package.json new file mode 100644 index 000000000..e24aa9572 --- /dev/null +++ b/smartpy/bts/package.json @@ -0,0 +1,26 @@ +{ + "name": "create-tezos-app", + "version": "1.0.3", + "description": "A Tezos Dapp Starter using Typescript and Taquito.", + "bin": "./bin/cli.js", + "scripts": { + "compile": "bash ./compile.sh", + "deploy": "npx ts-node scripts/deploy.ts", + "test" : "bash ./test.sh" + }, + "author": "Roshan Parajuli", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/0xrpj/create-tezos-dapp" + }, + "dependencies": { + "@taquito/signer": "^14.0.0", + "@taquito/taquito": "^14.0.0", + "dotenv": "^16.0.3", + "ts-node": "^10.9.1" + }, + "devDependencies": { + "typescript": "^4.8.4" + } +} \ No newline at end of file diff --git a/smartpy/bts/scripts/deploy.ts b/smartpy/bts/scripts/deploy.ts new file mode 100644 index 000000000..118a4f176 --- /dev/null +++ b/smartpy/bts/scripts/deploy.ts @@ -0,0 +1,60 @@ +import * as dotenv from "dotenv"; +import { TezosToolkit } from "@taquito/taquito"; +import { InMemorySigner } from "@taquito/signer"; +import { NETWORK } from "../config/config"; + +dotenv.config(); + +const deploy = async () => { + type networkTypes = keyof typeof NETWORK; + + const { ORIGINATOR_PRIVATE_KEY } = process.env; + let file = process.argv[2]; + let rpcType: networkTypes = process.argv[3] + ?.toUpperCase() + .slice(1) as networkTypes; + + console.info("Staring validation..."); + + if (!ORIGINATOR_PRIVATE_KEY) { + console.error("Private key missing in the environment."); + return; + } + + if (!file) { + console.log("Pass filename to deploy! Read the docs."); + return; + } + + if (!rpcType || !Object.keys(NETWORK).includes(rpcType)) { + console.log("Valid networks:", Object.keys(NETWORK)); + console.error("Invalid network"); + return; + } + + file = file.toLowerCase(); + + const signer = await InMemorySigner.fromSecretKey(ORIGINATOR_PRIVATE_KEY); + const Tezos = new TezosToolkit(NETWORK[rpcType].url); + Tezos.setProvider({ signer: signer }); + + console.log(`Validation checks out... \nDeploying the ${file} contract..\n`); + try { + const { hash, contractAddress } = await Tezos.contract.originate({ + code: require(`../contracts/build/${file}.json`), + init: require(`../contracts/build/${file}_storage.json`), + }); + + console.log(`::${contractAddress}`); + } catch (error) { + console.log( + `Oops... Deployment faced an issue.\nHere's the detailed info about the error,\n${error}` + ); + + console.log( + "Make sure of these things. \n1. Compiled contracts are inside the build folder.\n2. You have enough Tezos balance in the wallet for deployment." + ); + } +}; + +deploy(); diff --git a/smartpy/bts/test.sh b/smartpy/bts/test.sh new file mode 100755 index 000000000..34a7156ee --- /dev/null +++ b/smartpy/bts/test.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -e -o pipefail + +echo "----------------------------------------" +echo "Compiling contracts ... " +echo "----------------------------------------" + +# Expected location of SmartPy CLI. +SMART_PY_CLI=~/smartpy-cli/SmartPy.sh + +# Build artifact directory. +TEST_OUT_DIR=./contracts/build/.contract_build/test + +# Array of SmartPy files to compile. +# CONTRACTS_ARRAY=(counter) + +# Exit if SmartPy is not installed. +if [ ! -f "$SMART_PY_CLI" ]; then + echo "Fatal: Please install SmartPy CLI at $SMART_PY_CLI" && exit +fi + +function processContract { + CONTRACT_NAME=$1 + TEST_OUT_DIR=$2 + CONTRACT_IN_TEST="./contracts/tests/${CONTRACT_NAME}.py" + CONTRACT_OUT="${CONTRACT_NAME}.json" + STORAGE_OUT="${CONTRACT_NAME}_storage.json" + CONTRACT_COMPILED="${CONTRACT_NAME}/step_000_cont_0_contract.json" + STORAGE_COMPILED="${CONTRACT_NAME}/step_000_cont_0_storage.json" + + echo ">> Processing ${CONTRACT_NAME}" + + # Ensure file exists. + if [ ! -f "$CONTRACT_IN_TEST" ]; then + echo "Fatal: $CONTRACT_IN_TEST not found. Running from wrong dir?" && exit + fi + + echo ">>> Commencing the tests ${CONTRACT_NAME} ... " + $SMART_PY_CLI test $CONTRACT_IN_TEST $TEST_OUT_DIR --html +} + +export PYTHONPATH=$PWD + + +# Use if you want to pass a contract or more as arguments. +for n in $(seq 1 $#); do + processContract $1 $TEST_OUT_DIR + shift +done + + +echo "> Test Complete." \ No newline at end of file diff --git a/tezos-addresses/Dockerfile b/tezos-addresses/Dockerfile new file mode 100644 index 000000000..996e33dd7 --- /dev/null +++ b/tezos-addresses/Dockerfile @@ -0,0 +1,31 @@ +FROM golang:1.20-alpine +WORKDIR /app + +COPY . . +#RUN go mod download + +#RUN go get -u blockwatch.cc/tzgo + +#RUN go get blockwatch.cc/tzgo/contract + +#RUN go get blockwatch.cc/tzgo/micheline + +#RUN go get blockwatch.cc/tzgo/rpc + +#RUN go get blockwatch.cc/tzgo/signer + +#RUN go get blockwatch.cc/tzgo/tezos + +#RUN go get github.com/echa/log + +#RUN go get github.com/joho/godotenv + +RUN go mod tidy + +COPY *.go ./ + +RUN go build -o tezos-address + +EXPOSE 8080 + +CMD [ "./tezos-address" ] diff --git a/tezos-addresses/go.mod b/tezos-addresses/go.mod new file mode 100644 index 000000000..8aaf4a15e --- /dev/null +++ b/tezos-addresses/go.mod @@ -0,0 +1,21 @@ +module tezos-addresses + +go 1.20 + +require ( + blockwatch.cc/tzgo v1.16.6 + github.com/echa/log v1.2.2 + github.com/joho/godotenv v1.5.1 +) + +require ( + github.com/decred/dcrd/dcrec/secp256k1 v1.0.3 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0 // indirect + github.com/echa/bson v0.0.0-20220430141917-c0fbdf7f8b79 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect +) diff --git a/tezos-addresses/go.sum b/tezos-addresses/go.sum new file mode 100644 index 000000000..f787671e1 --- /dev/null +++ b/tezos-addresses/go.sum @@ -0,0 +1,48 @@ +blockwatch.cc/tzgo v1.16.6 h1:gh95gmFp4kJFFq6/qQzYbPbWjlrejyGFn2gmPa2J5C8= +blockwatch.cc/tzgo v1.16.6/go.mod h1:Bm3ZfCsqnJtpsAdwBQmhsoz4n8qc9qL4uJhsDoLArR8= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1 v1.0.3 h1:u4XpHqlscRolxPxt2YHrFBDVZYY1AK+KMV02H1r+HmU= +github.com/decred/dcrd/dcrec/secp256k1 v1.0.3/go.mod h1:eCL8H4MYYjRvsw2TuANvEOcVMFbmi9rt/6hJUWU5wlU= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0 h1:3GIJYXQDAKpLEFriGFN8SbSffak10UXHGdIcFaMPykY= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0/go.mod h1:3s92l0paYkZoIHuj4X93Teg/HB7eGM9x/zokGw+u4mY= +github.com/echa/bson v0.0.0-20220430141917-c0fbdf7f8b79 h1:J+/tX7s5mN1aoeQi2ySzix7+zyEhnymkudOxn7VMze4= +github.com/echa/bson v0.0.0-20220430141917-c0fbdf7f8b79/go.mod h1:Ih8Pfj34Z/kOmaLua+KtFWFK3AviGsH5siipj6Gmoa8= +github.com/echa/log v1.2.2 h1:tL0IxLI1SqreYWvnkpdE1exilCq9sCOp+aPZWWtwtFU= +github.com/echa/log v1.2.2/go.mod h1:MuBQcNxMgV0eT5iL3yvSZyu4wh40FKfmwJQs1RDUqcQ= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/bson.v2 v2.0.0-20171018101713-d8c8987b8862 h1:l7JQszYQzJc0GspaN+sivv8wScShqfkhS3nsgID8ees= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/tezos-addresses/main.go b/tezos-addresses/main.go new file mode 100755 index 000000000..994edaa39 --- /dev/null +++ b/tezos-addresses/main.go @@ -0,0 +1,556 @@ +package main + +import ( + "context" + "fmt" + "os" + + "blockwatch.cc/tzgo/contract" + "blockwatch.cc/tzgo/micheline" + "blockwatch.cc/tzgo/rpc" + "blockwatch.cc/tzgo/signer" + "blockwatch.cc/tzgo/tezos" + "github.com/echa/log" + "github.com/joho/godotenv" +) + +const ( + tzZeroAddress = "tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg" +) + +func main() { + rpc.UseLogger(log.Log) + + err := godotenv.Load(".env") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c, err := rpc.NewClient(os.Getenv("TEZOS_ENDPOINT"), nil) + fmt.Println("new client") + + fmt.Println(c.ChainId) + + if err != nil { + fmt.Println(err) + return + } + + err = c.Init(ctx) + + if err != nil { + fmt.Println(err) + return + } + + c.Listen() + + // deployment options + opts := rpc.DefaultOptions + opts.Signer = signer.NewFromKey(tezos.MustParsePrivateKey(os.Getenv("secret_deployer"))) + fmt.Println(os.Getenv("secret_deployer")) + + bmc_periphery := os.Getenv("BMC_PERIPHERY") + bmc_management := os.Getenv("BMC_MANAGEMENT") + btsCore := os.Getenv("BTS_CORE") + btsPeriphery := os.Getenv("BTS_PERIPHERY") + prim := micheline.Prim{} + + // bmc_periphery + + contractAddress := tezos.MustParseAddress(bmc_periphery) + bmcPeripheryClient := contract.NewContract(contractAddress, c) + + in := "{\"string\": \"" + bmc_management + "\" }" + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args := contract.NewTxArgs() + + entrypoint := "set_bmc_management_addr" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + from := tezos.MustParseAddress("tz1ZPVxKiybvbV1GvELRJJpyE1xj1UpNpXMv") + + argument := args.WithSource(from).WithDestination(contractAddress) + + fmt.Println("setting bmc management address in periphery....") + + res, err := bmcPeripheryClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + // ************************************************************************************************************************************* + // bts periphery + + contractAddress = tezos.MustParseAddress(btsPeriphery) + + btsPeripheryClient := contract.NewContract(contractAddress, c) + + in = "{\"string\": \"" + bmc_periphery + "\" }" + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "set_bmc_address" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + from = tezos.MustParseAddress("tz1ZPVxKiybvbV1GvELRJJpyE1xj1UpNpXMv") + + argument = args.WithSource(from).WithDestination(contractAddress) + + fmt.Println("setting bmc periphery in bts core....") + + res, err = btsPeripheryClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + // ************************************************************************************************************************************* + // bts periphery + + in = "{\"string\": \"" + btsCore + "\" }" + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "set_bts_core_address" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + from = tezos.MustParseAddress("tz1ZPVxKiybvbV1GvELRJJpyE1xj1UpNpXMv") + + argument = args.WithSource(from).WithDestination(contractAddress) + + fmt.Println("setting setting bts core in bts periphery....") + + res, err = btsPeripheryClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + // ************************************************************************************************************************************* + // bmc management + + contractAddress = tezos.MustParseAddress(bmc_management) + + bmcManagementClient := contract.NewContract(contractAddress, c) + + in = "{\"string\": \"" + bmc_periphery + "\" }" + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "set_bmc_periphery" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + from = tezos.MustParseAddress("tz1ZPVxKiybvbV1GvELRJJpyE1xj1UpNpXMv") + + argument = args.WithSource(from).WithDestination(contractAddress) + + fmt.Println("setting bmc periphery in bmc management....") + + res, err = bmcManagementClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + // ************************************************************************************************************************************* + // set btp address + + prim = micheline.Prim{} + + in = "{ \"string\": \"" + os.Getenv("TZ_NETWORK") + "\" }" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + fmt.Println("setting bmcBTP address in bmcManagement...") + + args = contract.NewTxArgs() + + entrypoint = "set_bmc_btp_address" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + argument = args.WithSource(from).WithDestination(contractAddress) + + res, err = bmcManagementClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + //*********************************************************************************************************************************** + // update bts periphery + + contractAddress = tezos.MustParseAddress(btsCore) + btsCoreClient := contract.NewContract(contractAddress, c) + + prim = micheline.Prim{} + + in = "{ \"string\": \"" + btsPeriphery + "\" }" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + fmt.Println("setting bts periphery in btsCoreClient...") + + args = contract.NewTxArgs() + + entrypoint = "update_bts_periphery" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + argument = args.WithSource(from).WithDestination(contractAddress) + + res, err = btsCoreClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + //*********************************************************************************************************************************** + // add service + + prim = micheline.Prim{} + + in = "{ \"prim\": \"Pair\", \"args\": [ { \"string\": \"" + btsPeriphery + "\" }, { \"string\": \"bts\" } ] }" + + contractAddress = tezos.MustParseAddress(bmc_management) + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "add_service" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + argument = args.WithSource(from).WithDestination(contractAddress) + fmt.Println("adding service...") + + res, err = bmcManagementClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + //*********************************************************************************************************************************** + // set fee ratio + + prim = micheline.Prim{} + contractAddress = tezos.MustParseAddress(btsCore) + + in = "{ \"prim\": \"Pair\", \"args\": [ { \"int\": \"100\" }, { \"prim\": \"Pair\", \"args\": [ { \"int\": \"450\" }, { \"string\": \"" + os.Getenv("TZ_NATIVE_COIN_NAME") + "\" } ] } ] }" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "set_fee_ratio" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + argument = args.WithSource(from).WithDestination(contractAddress) + + fmt.Println("setting fee ratio...") + res, err = btsCoreClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + //*********************************************************************************************************************************** + // add route + + prim = micheline.Prim{} + + link := "btp://" + os.Getenv("ICON_NETWORK") + "/" + os.Getenv("ICON_BMC") + fmt.Println(link) + + in = "{ \"prim\": \"Pair\", \"args\": [ { \"string\": \"" + link + "\" }, { \"string\": \"" + link + "\" } ] }" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "add_route" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + argument = args.WithSource(from).WithDestination(bmcManagementClient.Address()) + + fmt.Println("adding route...") + res, err = bmcManagementClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + //*********************************************************************************************************************************** + // add link + + prim = micheline.Prim{} + + fmt.Println(link) + + in = "{ \"string\": \"" + link + "\" }" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "add_link" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + argument = args.WithSource(from).WithDestination(bmcManagementClient.Address()) + + fmt.Println("adding link....") + + res, err = bmcManagementClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + //*********************************************************************************************************************************** + // set link rx height + + prim = micheline.Prim{} + + fmt.Println(os.Getenv("ICON_RX_HEIGHT")) + + in = "{ \"prim\": \"Pair\", \"args\": [ { \"int\": \"" + os.Getenv("ICON_RX_HEIGHT") + "\" }, { \"string\": \"" + link + "\" } ] }" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "set_link_rx_height" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + argument = args.WithSource(from).WithDestination(bmcManagementClient.Address()) + + fmt.Println("setting link_rx_height...") + res, err = bmcManagementClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) + + //*********************************************************************************************************************************** + // add relay + + prim = micheline.Prim{} + + fmt.Println(os.Getenv("RELAYER_ADDRESS")) + + in = "{ \"prim\": \"Pair\", \"args\": [ [ { \"string\": \"" + os.Getenv("RELAYER_ADDRESS") + "\" } ], { \"string\": \"" + link + "\" } ] }" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "add_relay" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + argument = args.WithSource(from).WithDestination(bmcManagementClient.Address()) + + fmt.Println("adding relay...") + res, err = bmcManagementClient.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + //*********************************************************************************************************************************** + // register native coin + // fmt.Println(res) + + // register(btsCoreClient.Address(), os.Getenv("ICON_NATIVE_COIN_NAME"), opts) + + //*********************************************************************************************************************************** + // add relay + // fmt.Println(res) + + prim = micheline.Prim{} + + in = "{\"prim\": \"Unit\"}" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args = contract.NewTxArgs() + + entrypoint = "toggle_bridge_on" + + args.WithParameters(micheline.Parameters{Entrypoint: entrypoint, Value: prim}) + + argument = args.WithSource(from).WithDestination(bmcManagementClient.Address()) + + fmt.Println("toggling bridge on bmc management") + opts.IgnoreLimits = false + res, err = bmcManagementClient.Call(ctx, argument, &opts) + fmt.Println(res) + + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + fmt.Println("toggling bridge on bts core") + res, err = btsCoreClient.Call(ctx, argument, &opts) + if err != nil { + fmt.Println("error while calling", entrypoint) + fmt.Println(err) + } + + fmt.Println(res) +} + +func register(btsCore tezos.Address, coinName string, opts rpc.CallOptions) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c, err := rpc.NewClient("https://ghostnet.smartpy.io", nil) + + if err != nil { + fmt.Println(err) + return + } + + err = c.Init(ctx) + + if err != nil { + fmt.Println(err) + return + } + + c.Listen() + + con := contract.NewContract(btsCore, c) + + if err = con.Resolve(ctx); err != nil { + fmt.Println(err) + return + } + + var prim micheline.Prim + + in := "{\"prim\": \"Pair\", \"args\": [{\"prim\": \"Pair\",\"args\": [{\"string\": \"" + "tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg" + "\"},{\"prim\": \"Pair\",\"args\": [{\"int\": \"0\"},{\"int\": \"0\"}]}]},{\"prim\": \"Pair\",\"args\": [[],{\"prim\": \"Pair\",\"args\": [{\"string\": \"" + coinName + "1" + "\"},[]]}]}]}" + + if err := prim.UnmarshalJSON([]byte(in)); err != nil { + fmt.Println("couldnot unmarshall empty string") + fmt.Println(err) + return + } + + args := contract.NewTxArgs() + + args.WithParameters(micheline.Parameters{Entrypoint: "register", Value: prim}) + + from := tezos.MustParseAddress("tz1ZPVxKiybvbV1GvELRJJpyE1xj1UpNpXMv") + + argument := args.WithSource(from).WithDestination(btsCore) + + // ags := con.AsFA1() + opts.IgnoreLimits = true + opts.MaxFee = 10000000 + res, err := con.Call(ctx, argument, &opts) + + if err != nil { + fmt.Println("error while calling") + fmt.Println(err) + } + + fmt.Println(res) +}