Skip to content

Commit

Permalink
nft: Add Go programs and tests
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Sover <dan.sover@avalabs.org>
  • Loading branch information
exdx committed Oct 6, 2023
1 parent 834bc79 commit 3eea9c6
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 2 deletions.
111 changes: 111 additions & 0 deletions x/programs/examples/nft.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package examples

import (
"context"
"fmt"

"github.com/ava-labs/avalanchego/utils/logging"

"github.com/ava-labs/hypersdk/crypto/ed25519"
"github.com/ava-labs/hypersdk/x/programs/runtime"
"github.com/ava-labs/hypersdk/x/programs/utils"

"go.uber.org/zap"
)

func NewNFT(log logging.Logger, programBytes []byte, maxFee uint64, costMap map[string]uint64, m Metadata) *NFT {
return &NFT{
log: log,
programBytes: programBytes,
metadata: m,
maxFee: maxFee,
costMap: costMap,
}
}

type NFT struct {
log logging.Logger
programBytes []byte
metadata Metadata
// metering
maxFee uint64
costMap map[string]uint64
}

type Metadata struct {
Name string
Symbol string
URI string
}

func (t *NFT) Run(ctx context.Context) error {
meter := runtime.NewMeter(t.log, t.maxFee, t.costMap)
db := utils.NewTestDB()
store := newProgramStorage(db)

runtime := runtime.New(t.log, meter, store)
contractId, err := runtime.Create(ctx, t.programBytes)
if err != nil {
return err
}

t.log.Debug("initial cost",
zap.Int("gas", 0),
)

// Generate keys for Alice
// She deploys the NFT contract and is the original owner
alicePtr, _, err := nftKeyPtr(ctx, runtime)
if err != nil {
return err
}

_, err = runtime.Call(ctx, "init", contractId, alicePtr)
if err != nil {
return err
}
t.log.Debug("init called",
zap.String("alicePtr", fmt.Sprint(alicePtr)),
)

// mint 1 token, send to alice
mintAlice := uint64(1)
_, err = runtime.Call(ctx, "mint", contractId, alicePtr, mintAlice)
if err != nil {
return err
}
t.log.Debug("minted",
zap.Uint64("alice", mintAlice),
)

// Generate keys for Bob
bobPtr, _, err := nftKeyPtr(ctx, runtime)
if err != nil {
return err
}

// transfer NFT from Alice to Bob
_, err = runtime.Call(ctx, "transfer", contractId, alicePtr, bobPtr)
if err != nil {
return err
}
t.log.Debug("transfer",
zap.Uint64("alice", mintAlice),
)

return nil
}

func nftKeyPtr(ctx context.Context, runtime runtime.Runtime) (uint64, ed25519.PublicKey, error) {
priv, err := ed25519.GeneratePrivateKey()
if err != nil {
return 0, ed25519.EmptyPublicKey, err
}

pk := priv.PublicKey()
ptr, err := runtime.WriteGuestBuffer(ctx, pk[:])
return ptr, pk, err
}
62 changes: 62 additions & 0 deletions x/programs/examples/nft_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package examples

import (
"context"
_ "embed"
"os"
"testing"

"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/utils/logging"
)

var (
//go:embed testdata/token.wasm
nftProgramBytes []byte

// example cost map
nftCostMap = map[string]uint64{
"ConstI32 0x0": 1,
"ConstI64 0x0": 2,
}
nftMaxGas uint64 = 13000
nftLog = logging.NewLogger(
"",
logging.NewWrappedCore(
logging.Debug,
os.Stderr,
logging.Plain.ConsoleEncoder(),
))

metadata = Metadata{
Name: "My NFT",
Symbol: "MNFT",
URI: "ipfs://my-nft.jpg",
}
)

// go test -v -timeout 30s -run ^TestNFTProgram$ github.com/ava-labs/hypersdk/x/programs/examples
func TestNFTProgram(t *testing.T) {
require := require.New(t)

program := NewNFT(nftLog, nftProgramBytes, nftMaxGas, nftCostMap, metadata)
err := program.Run(context.Background())
require.NoError(err)
}

// go test -v -benchmark -run=^$ -bench ^BenchmarkNFTProgram$ github.com/ava-labs/hypersdk/x/programs/examples -memprofile benchvset.mem -cpuprofile benchvset.cpu
func BenchmarkNFTProgram(b *testing.B) {
require := require.New(b)
program := NewNFT(nftLog, nftProgramBytes, nftMaxGas, nftCostMap, metadata)
b.ResetTimer()
b.Run("benchmark_nft_program", func(b *testing.B) {
for i := 0; i < b.N; i++ {
err := program.Run(context.Background())
require.NoError(err)
}
})
}
4 changes: 2 additions & 2 deletions x/programs/rust/examples/nft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//! The program serves as a non-fungible token with the ability to mint, burn, and transfer.
//! Only supports whole units with no decimal places.
//!
//! NOTE: The NFT must support the common NFT metadata format.
//! This JSON encoded file provides all the necessary metadata about the NFT.
//! The NFT must support the common NFT metadata format.
//! This implementation is for a 1 of 1 singleton NFT. The total supply should be 1.
use metadata::Nft;
use wasmlanche_sdk::{program::Program, public, state_keys, types::Address};

Expand Down

0 comments on commit 3eea9c6

Please sign in to comment.