-
Notifications
You must be signed in to change notification settings - Fork 30
add digital asset client with create_collection and mint capabilities #170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to add unit tests on some of the functions.
Additionally, run gofumpt -l -w .
to format and golangci-lint run
to lint.
It's looking pretty good though
digital_asset_client.go
Outdated
// SerializeString serializes a single string using the BCS format | ||
func SerializeString(input string) ([]byte, error) { | ||
return bcs.SerializeSingle(func(ser *bcs.Serializer) { | ||
ser.WriteString(input) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add this to bcs
if it isn't there already
digital_asset_client.go
Outdated
// === COLLECTION OPERATIONS === | ||
|
||
// CreateCollection creates a new collection using the Digital Asset standard | ||
func (dac *DigitalAssetClient) CreateCollection(creator *Account, options CreateCollectionOptions) (*string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might not need to return *string
, you can probably just return string
and use ""
as a failure mode return type. It's standard in Go, though I'm not sure I'm attached to either.
digital_asset_client.go
Outdated
descriptionBytes, err := SerializeString(options.Description) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to serialize description: %w", err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's times like this, I wish Go had throws, so it wouldn't have to be duplicated for each of these.
I would normally say, make this all into a function, so you don't duplicate the error message.
// Build the transaction | ||
var functionName string | ||
var args [][]byte | ||
|
||
if options.IsSoulBound { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this breakdown
digital_asset_client.go
Outdated
functionName = "mint_soul_bound" | ||
|
||
// Serialize the soul_bound_to address | ||
recipientBytes, err := bcs.Serialize(&recipient) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to serialize recipient address: %w", err) | ||
} | ||
|
||
// mint_soul_bound requires the recipient address as the last argument | ||
args = [][]byte{ | ||
collectionBytes, | ||
descriptionBytes, | ||
nameBytes, | ||
uriBytes, | ||
propertyKeysBytes, | ||
propertyTypesBytes, | ||
propertyValuesBytes, | ||
recipientBytes, // soul_bound_to: address | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would probably move pieces like this to an internal function (lowercase starting letter) e.g. mintSoulboundArgs()
digital_asset_client.go
Outdated
case "address": | ||
// For address, decode hex and use as 32-byte array | ||
addressStr := strings.TrimPrefix(value, "0x") | ||
addressBytes, parseErr := hex.DecodeString(addressStr) | ||
if parseErr != nil { | ||
return nil, fmt.Errorf("failed to parse address value '%s': %w", value, parseErr) | ||
} | ||
// Ensure it's 32 bytes | ||
if len(addressBytes) < 32 { | ||
padded := make([]byte, 32) | ||
copy(padded[32-len(addressBytes):], addressBytes) | ||
addressBytes = padded | ||
} | ||
valueBytes = addressBytes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to write a parser, there's already a parser for address
digital_asset_client.go
Outdated
case "bool": | ||
// For bool, parse and BCS serialize as bool | ||
boolValue, parseErr := strconv.ParseBool(value) | ||
if parseErr != nil { | ||
return nil, fmt.Errorf("failed to parse bool value '%s': %w", value, parseErr) | ||
} | ||
valueBytes, err = bcs.SerializeSingle(func(s *bcs.Serializer) { | ||
s.Bool(boolValue) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There actually should already be convertArg
to convert all of these types already
// For byte vector, decode from hex or use as UTF-8, then BCS serialize | ||
var bytesValue []byte | ||
if strings.HasPrefix(value, "0x") { | ||
bytesValue, err = hex.DecodeString(value[2:]) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to parse hex bytes value '%s': %w", value, err) | ||
} | ||
} else { | ||
bytesValue = []byte(value) | ||
} | ||
// BCS serialize the byte vector | ||
valueBytes, err = bcs.SerializeSingle(func(s *bcs.Serializer) { | ||
s.WriteBytes(bytesValue) | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, I would use the existing convertArg
digital_asset_client.go
Outdated
default: | ||
// For unknown types, fall back to BCS string | ||
fmt.Printf("Warning: unknown property type '%s', treating as Move String\n", propertyType) | ||
valueBytes, err = bcs.SerializeSingle(func(s *bcs.Serializer) { | ||
s.WriteString(value) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... maybe want to error out, rather than write something that might not parse
examples/digital_asset/main.go
Outdated
) | ||
|
||
const ( | ||
testEd25519PrivateKey = "ed25519-priv-0xc5338cd251c22daa8c9c9cc94f498cc8a5c7e1d2e75287a5dda91096fe64efa5" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can also just generate new keys each time, unless you need something consistent.
// ObjectTypeTag is the TypeTag for 0x1::object::ObjectCore | ||
var ObjectTypeTag = TypeTag{&StructTag{ | ||
Address: AccountOne, | ||
Module: "object", | ||
Name: "ObjectCore", | ||
}} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would rename to ObjectCoreTypeTag
// tokenAddress := // Placeholder | ||
|
||
// transferTxHash, err := daClient.TransferToken(creator, tokenAddress, collector.AccountAddress()) | ||
// if err != nil { | ||
// fmt.Printf("Warning: Failed to transfer NFT (expected without proper token address): %v\n", err) | ||
// } else { | ||
// fmt.Printf("NFT transferred with transaction hash: %s\n", *transferTxHash) | ||
|
||
// // Wait for transfer | ||
// _, err = client.WaitForTransaction(*transferTxHash) | ||
// if err != nil { | ||
// panic("Failed to wait for transfer:" + err.Error()) | ||
// } | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can just remove the commented code
// main This example shows how to create and transfer digital assets (NFTs) | ||
func main() { | ||
// Run the main example | ||
example(aptos.DevnetConfig) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may have to add a test file so it runs in CI. Look at the other examples
var valueBytes []byte | ||
var err error | ||
|
||
switch propertyType { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to use the convertArg
to convert it into the correct type for each. Then we don't need copied serialization methods
func mintSoulboundArgs(recipient AccountAddress, collectionBytes []byte, descriptionBytes []byte, nameBytes []byte, uriBytes []byte, propertyKeysBytes []byte, propertyTypesBytes []byte, propertyValuesBytes []byte) (string, [][]byte, error) { | ||
functionName := "mint_soul_bound" | ||
|
||
// Serialize the soul_bound_to address | ||
recipientBytes, err := bcs.Serialize(&recipient) | ||
if err != nil { | ||
return "", nil, fmt.Errorf("failed to serialize recipient: %w", err) | ||
} | ||
|
||
// mint_soul_bound requires the recipient address as the last argument | ||
args := [][]byte{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably take in the types, and convert them instead in the args functions, so you don't have to pass in a large list of bytes (where you might get one out of order)
Update changelog please |
valueBytes, err = bcs.SerializeU8(numValue) | ||
|
||
case "u16": | ||
// For u8, parse and BCS serialize as u8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Repeated comments
// For u8, parse and BCS serialize as u8 | ||
numValue, parseErr := ConvertToU16(value) | ||
if parseErr != nil { | ||
return nil, fmt.Errorf("failed to parse u8 value '%s': %w", value, parseErr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these as well
Description
Add digital asset client with create_collection and mint capabilities. Also added an examples file to test out the flow.
Test Plan
Test file is yet to be added. + Functionality like transfer & indexing is remaining
Related Links