07-5SOL钱包开发流程

一 SOL简介

名称:

精度:

确认数:xxx块后交易安全不易会滚

是否支持token代币:是

是否支持质押:是

出块时间:

地址:Base58

交易签名算法

  • secp256k1
  • ed25519 BLAKE2b
  • schnorr_1
  • 其它签名算法

二 离线地址生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func GenerateOfflineAddress() types.Account {
// 生成随机墒---bit39
entropy, _ := bip39.NewEntropy(256)
// 墒生成助记词
mnemonic, _ := bip39.NewMnemonic(entropy)

// 从助记词生成种子

logrus.Infof("mnemonic: %s", mnemonic)
seed := bip39.NewSeed(mnemonic, "")

path := `m/44'/501'/0'/0'`
derivedKey, _ := hdwallet.Derived(path, seed)
//accountFromSeed, err := types.AccountFromBytes(derivedKey)
accountFromSeed, err := types.AccountFromSeed(derivedKey.PrivateKey)
if err != nil {
fmt.Printf("err=%v", err)
}
publicKey := accountFromSeed.PublicKey.ToBase58()
fmt.Println("Solana Wallet Address:", publicKey)
return accountFromSeed
}

三 离线签名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NewTx := types.NewTransactionParam{
Message: types.NewMessage(types.NewMessageParam{
FeePayer: Account.PublicKey,
RecentBlockhash: block.Blockhash,
Instructions: []types.Instruction{system.Transfer(system.TransferParam{
From: Account.PublicKey,
To: common.PublicKeyFromString("6cPnfGr9Y4bZK7ykNpxe2hkKfaPPgsy6Tu5ahyGhzQLt"),
Amount: 123000,
})},
}),
Signers: []types.Account{Account},
}

tx, err := types.NewTransaction(NewTx)
if err != nil {
logrus.Warnf("new transaction error: %s", err)
}

hash, err := solClient.SendTransaction(bg, tx)
if err != nil {
logrus.Warnf("new transaction error: %s", err)
}
logrus.Infof("hash: %v", hash)

三、交易理解

sol转账交易

交易包括一个或多个 指令,每个代表要处理的特定操作。指令的执行逻辑存储在部署到Solana网络的程序上,每个程序都存储自己的指令集。

为简单起见,可以将交易视为处理一个或多个说明的请求

s

注意:会按照顺序执行,要么全部通过,要么全部不通过

Instruction 指令,下面是单个指令的示意图

s

指令中包含:

  • System Program
  • Accounts:帐户信息:Sender 地址 和 Receiver 地址
  • Transfer Amount 转账金额,注意:给一个新地址转账的时候,必须要大于等于租金费用890880 也就是0.00089088,满足租金后就可以随便转账了,可以通过GetMinimumBalanceForRentExemption接口拿到这个金额

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

// SendSol 用于组装sol币的的交易
func SendSol() {
// 拆解
DEV := "https://solana-devnet.g.alchemy.com/v2/wqZxT7UnY6AgrzV42CtGgGQ7ZGM-UrTq"
c := client.NewClient(DEV)
// 1、导入私钥恢复帐户
piv := "46M2pAp4z3mNPuTh7jS8XHSn69TC4FAnX33Avjx7wqy3W1zzZKiSoBmNTH5PEBDKu7xR2rPa9ocSyzGWYFK7VRF2"
alice, err := types.AccountFromBase58(piv)
if err != nil {
fmt.Printf("err=%v", err)
}
bob := "6cPnfGr9Y4bZK7ykNpxe2hkKfaPPgsy6Tu5ahyGhzQLt"

// 查询下地址的当前余额
balances, err := c.GetBalance(context.Background(), alice.PublicKey.String())
if err != nil {
fmt.Printf("get balances err = %v", err)
}
fmt.Printf("balances = %v\n", balances)
// 2、组装交易
// 2.1 拿recentBlockhashResponse
recentBlockhashResponse, err := c.GetLatestBlockhash(context.Background())
if err != nil {
log.Fatalf("failed to get recent blockhash, err: %v", err)
}
// 最小转账金额
minimumBalanceForRentExemption, err := c.GetMinimumBalanceForRentExemption(context.Background(), 0)
fmt.Printf("minimumBalanceForRentExemption = %v\n", minimumBalanceForRentExemption)
// 2.2 定义Instruction然后填充它
ins := make([]types.Instruction, 0, 2)
// createAccount
//createAccountInstruction := system.CreateAccount(
// system.CreateAccountParam{
// From: alice.PublicKey,
// New: common.PublicKeyFromString(alice.PublicKey.String()),
// Owner: common.StakeProgramID,
// Lamports: nonceAccountMinimumBalance,
// Space: system.NonceAccountSize,
// })
//ins = append(ins, createAccountInstruction)
// 交易分成2个大部份 Transactions 和 Instructions 指令,Instructions可以有多个
// 这里我们先做一个简单的交易 ins就是Instructions\
ins = append(ins, system.Transfer(
system.TransferParam{
From: common.PublicKeyFromString(alice.PublicKey.String()),
To: common.PublicKeyFromString(bob),
Amount: minimumBalanceForRentExemption,
}))
message := types.NewMessage(
types.NewMessageParam{
FeePayer: common.PublicKeyFromString(alice.PublicKey.String()),
Instructions: ins,
RecentBlockhash: recentBlockhashResponse.Blockhash,
})
tx, err := types.NewTransaction(types.NewTransactionParam{
Message: message,
Signers: []types.Account{alice},
})
if err != nil {
log.Fatalf("failed to new transaction, err: %v", err)
}
// 3、广播上链
txhash, err := c.SendTransaction(context.Background(), tx)
if err != nil {
log.Fatalf("failed to SendTransaction, err: %v", err)
}
fmt.Println("tx hash", txhash)
}

让我们来看看转账指令发生了什么

s

可以看到,内部是由发送者Sender 地址的Lamprots帐户转给了 和 Receiver 地址的Lamprots帐户,因为sol一切皆为帐户

继续往下看,交易由什么组成,由 签名Signatures + 消息Message组成

s

消息Message里包含了什么?4个东西

s

Token的转账

token转账的关键就是拿到发送者和接收者在这个代币合约上的地址

还有要确定接收者的子帐户(在这个token上的地址)是否上链,如果没有上过链(新地址)则需要为其创建对应的子帐户地址用于接收token

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// SendToken 用于组装Token代币的的交易
func SendToken() {

tokenAddress := "58g1sdTgzazjR4ApXxPP63JRHj4ZDXePX251An7fxEhG"
DEV := "https://solana-devnet.g.alchemy.com/v2/wqZxT7UnY6AgrzV42CtGgGQ7ZGM-UrTq"
c := client.NewClient(DEV)
// 1、获取代币的owner 拥有者
//accInfo, err := c.GetAccountInfo(context.Background(), tokenAddress)
//if err != nil {
// log.Fatalf("failed to GetAccountInfo, err: %v", err)
//}
//
//fmt.Printf("accInfo Owner =%v\n", accInfo.Owner)
// 2 寻找用户在这个token上的地址
piv := "46M2pAp4z3mNPuTh7jS8XHSn69TC4FAnX33Avjx7wqy3W1zzZKiSoBmNTH5PEBDKu7xR2rPa9ocSyzGWYFK7VRF2"
alice, err := types.AccountFromBase58(piv)
if err != nil {
fmt.Printf("err=%v", err)
}
bob := "6cPnfGr9Y4bZK7ykNpxe2hkKfaPPgsy6Tu5ahyGhzQLt"
//bob := "8pZWotbBSKBy6Luxf41TxUxDvZHp49jAbuUe2yFZRorE"
fromTokenATA, _, err := common.FindAssociatedTokenAddress(alice.PublicKey, common.PublicKeyFromString(tokenAddress))
if err != nil {
log.Fatalf("failed to FindAssociatedTokenAddress, err: %v", err)
}
//fmt.Printf("fromTokenATA=%v\n", fromTokenATA)
toTokenATA, _, err := common.FindAssociatedTokenAddress(common.PublicKeyFromString(bob), common.PublicKeyFromString(tokenAddress))
if err != nil {
log.Fatalf("failed to FindAssociatedTokenAddress, err: %v", err)
}
//fmt.Printf("toTokenATA=%v\n", toTokenATA)
ins := make([]types.Instruction, 0, 2)
recentBlockhashResponse, err := c.GetLatestBlockhash(context.Background())
if err != nil {
log.Fatalf("failed to get recent blockhash, err: %v", err)
}
info, err := c.GetTokenAccount(context.Background(), toTokenATA.ToBase58())
if err != nil {
if errors.Is(err, token.ErrInvalidAccountOwner) {
fmt.Println("test")
ins = append(ins, associated_token_account.Create(
associated_token_account.CreateParam{
Funder: common.PublicKeyFromString(alice.PublicKey.String()),
Owner: common.PublicKeyFromString(bob),
Mint: common.PublicKeyFromString(tokenAddress),
AssociatedTokenAccount: toTokenATA,
//ProgramID: programID,
}))

} else {
log.Fatalf("failed to GetTokenAccount, err: %v", err)
}
} else {
if info.Owner.ToBase58() != bob {
log.Fatalf("failed to GetTokenAccount,info!=bob err: %v", err)
}
}

// 追加优先费
ComputerUnitPrice := uint64(110000) //一个计算单元的价格
ComputerUnitLimit := uint32(200000) //计算单元的限制
ins = append(ins, compute_budget.SetComputeUnitPrice(compute_budget.SetComputeUnitPriceParam{
MicroLamports: ComputerUnitPrice,
}))

ins = append(ins, compute_budget.SetComputeUnitLimit(compute_budget.SetComputeUnitLimitParam{
Units: ComputerUnitLimit,
}))
ins = append(ins, token.TransferChecked(
token.TransferCheckedParam{
From: fromTokenATA,
To: toTokenATA,
Mint: common.PublicKeyFromString(tokenAddress),
Auth: alice.PublicKey,
Signers: []common.PublicKey{alice.PublicKey},
Amount: 40000000000,
Decimals: 9, //精度

}),
)

message := types.NewMessage(
types.NewMessageParam{
FeePayer: common.PublicKeyFromString(alice.PublicKey.String()),
Instructions: ins,
RecentBlockhash: recentBlockhashResponse.Blockhash,
})
tx, err := types.NewTransaction(types.NewTransactionParam{
Message: message,
Signers: []types.Account{alice},
})

if err != nil {
log.Fatalf("failed to new transaction, err: %v", err)
}
// 3、广播上链
txhash, err := c.SendTransaction(context.Background(), tx)
if err != nil {
log.Fatalf("failed to SendTransaction, err: %v", err)
}
fmt.Println("tx hash", txhash)
}

四 SOL钱包开发中的API

1.获取账户信息

2.获取 recentBlochHash, 直接签名的话 recentBlochHash 相当于 nonce

3. 获取准备 nonce 账户的 Minimum Balance For Rent 数据

4. 获取最新块高 (Slot)

5. 根据块高获取交易

6. 根据交易 Hash 获取交易详情

7.发送交易到区块链网络

五 中心化钱包开发

六、去中心化钱包开发 HD钱包

七、总结

八、附件

资料;

节点:https://dashboard.alchemy.com/ 第三方节点

浏览器:https://solscan.io/

第三方SDK:https://github.com/blocto/solana-go-sdk

文档:https://solana.com/zh/docs

API/Swager 文档:https://solana.com/zh/docs/rpc

1
2
3


solana1.jpg

额外笔记

交易

我们发送到 Solana 网络中的一笔交易包括四个部分:

  • 一个或多个指令 (instructions
  • 一个要读取或写入的账户数组 (account_keys
  • 一个或多个签名 (signatures
  • 最近的区块哈希 (recent_blockhash

一个指令是 Solana 上最小的执行逻辑。指令指定了执行程序、涉及的所有账户和操作数据。指令调用程序更新状态(例如,调用代币程序将代币从你的帐户转移到另一个帐户),程序解释指令中的数据,并对指定账户进行操作

指令—>类似于以太坊智能合约上的函数调用

交易费—基础手续费 和 优先费

lamports_per_signature),基础手续费目前设定为每个签名 0.000005 SOL(5k lamports),这是一次性的费用,以便获得使用网络资源的权利,无论实际使用多少资源来执行交易(或者交易是否执行),都需要预先支付给网络。50% 的费用支付给产生区块的验证节点,剩余的 50% 则被销毁。

如果想提高其交易的优先级【可选的费用】,它可以设置一个 “计算单元价格”。 这个价格与 计算单元限制 结合使用,用来确定交易的优先级费用。

默认的计算单元限制为 每个指令 20 万 CU, 如果计算量比较大,可以设置最大为 140 万 Cu。 Solana 交易会预先请求指定数量的计算单元(CUs),如果超出这个数量,交易将失败。

另外,Solana 交易还会收交易包大小的限制,Solana 网络遵循最大传输单元 (MTU) 大小为 1280 字节,这与 IPv6 MTU 大小约束一致,以确保通过 UDP 传输集群信息的快速和可靠性。在计算必要的标头(IPv6 的 40 字节和 8 字节的片段头)后,1232 字节仍然可用于数据包, 签名和消息的组合不能超过此限制。

以下官方视频介绍

Accounts

sol Accounts 模型

Solana 上的宗旨是一切皆为帐户,类似Linux 的一切皆文件一样,

1
2
3
4
5
6
7
{
"key":number,// 视为区块链公共地址
"lamports":number, // 实际总余额 本地soltoken 的最小单位,1 lamports=0.0000000001sol
"data":Unit8Array,// 无符号整数存储的原始字节
"is_executable":boolean, // 是否为程序,如果为true 就是可执行程序,flase就是普通数据帐户
"ower":Publickey // 所有者,只有该帐户的所有者才可以更新该帐户内数据
}

Programs

Programs 程序就是solana 上的智能合约Smart contracts on Solana

  • 程序是无状态的,只能读取和写入别人的帐户数据

  • 必须是帐户所有者才能修改

  • 程序运行指令

  • 程序能发送指令给其他程序

总结

  • 一切皆程序
  • 所有帐户都持有SOL
  • 帐户能存储任意数据
  • 帐户也能存储可执行程序
  • 帐户也能床底到程序中并行执行

Programs Instructions

程序指令

原始模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"program_id":number,// 指定程序的id,该指令用于的程序
"keys":Array<{ // 设计指令的帐户,这里用了数组,说明是可以传递多个的
"key":Publcky,
"is_mutable":boolean,
"is_signer":boolean,
}>,
"data":Unit8Array //你有一定量的数据通过原始字节的形式发送到网络中
}


// 下面是go中的
type Instruction struct {
ProgramID common.PublicKey
Accounts []AccountMeta
Data []byte
}

type AccountMeta struct {
PubKey common.PublicKey
IsSigner bool
IsWritable bool
}

简单指令

多个指令捆绑在一起,就可以组成一个交易

Transactions 交易

1
2
3
4
5
6
7
8
9
10
message := types.NewMessage(
types.NewMessageParam{
FeePayer: common.PublicKeyFromString(alice.PublicKey.String()),
Instructions: ins,
RecentBlockhash: recentBlockhashResponse.Blockhash,
})
tx, err := types.NewTransaction(types.NewTransactionParam{
Message: message,
Signers: []types.Account{alice},
})

创建帐户的指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
func CreateAccountSimpleTx() {
c := NewClient()
alice, err := types.AccountFromBase58(Piv)
if err != nil {
fmt.Printf("err=%v", err)
}
fmt.Printf("alice=%v\n", alice.PublicKey.String())
// 查询下地址的当前余额
balances, err := c.GetBalance(context.Background(), alice.PublicKey.String())
if err != nil {
fmt.Printf("get balances err = %v", err)
}
fmt.Printf("balances = %v\n", balances)
// 2、组装交易
// 2.1 拿recentBlockhashResponse
recentBlockhashResponse, err := c.GetLatestBlockhash(context.Background())
if err != nil {
log.Fatalf("failed to get recent blockhash, err: %v", err)
}
fmt.Printf("recentBlockhashResponse = %v\n", recentBlockhashResponse)

// 最小转账金额
minimumBalanceForRentExemption, err := c.GetMinimumBalanceForRentExemption(context.Background(), 0)
fmt.Printf("minimumBalanceForRentExemption = %v\n", minimumBalanceForRentExemption)
// 2.2 定义Instruction然后填充它
ins := make([]types.Instruction, 0, 2)
// 交易分成2个大部份 Transactions 和 Instructions 指令,Instructions可以有多个
// 追加优先费
ComputerUnitPrice := uint64(200000) //一个计算单元的价格
ComputerUnitLimit := uint32(200000) //计算单元的限制
ins = append(ins, compute_budget.SetComputeUnitPrice(compute_budget.SetComputeUnitPriceParam{
MicroLamports: ComputerUnitPrice,
}))

ins = append(ins, compute_budget.SetComputeUnitLimit(compute_budget.SetComputeUnitLimitParam{
Units: ComputerUnitLimit,
}))

// DOINGS 我在做的事情是创建一个地址并为其在链上创建一个账号,参考createAccount方法
// 生成一个钱包,先不管他的地址
bob := types.NewAccount()
fmt.Printf("bob address=%s\n", bob.PublicKey.String())
// 指令三纬度,ProgramId 、 Accounts、Data
Accounts := []types.AccountMeta{
{PubKey: common.PublicKeyFromString(alice.PublicKey.String()), IsSigner: true, IsWritable: true},
{PubKey: common.PublicKeyFromString(bob.PublicKey.String()), IsSigner: true, IsWritable: true},
}
data, err := bincode.SerializeData(struct {
Instruction system.Instruction
Lamports uint64
Space uint64
Owner common.PublicKey
}{
Instruction: system.InstructionCreateAccount,
Lamports: minimumBalanceForRentExemption + 1,
//Space: system.NonceAccountSize,
Space: 0,
Owner: common.SystemProgramID,
})
createAccountIns := types.Instruction{
ProgramID: common.SystemProgramID,
Accounts: Accounts,
//Data: []byte{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 0, 0, 0, 0, 6, 161, 216, 23, 145, 55, 84, 42, 152, 52, 55, 189, 254, 42, 122, 178, 85, 127, 83, 92, 138, 120, 114, 43, 104, 164, 157, 192, 0, 0, 0, 0},
Data: data,
}
ins = append(ins, createAccountIns)
// 消息包含指令、区块哈希、fee支付者
message := types.NewMessage(
types.NewMessageParam{
FeePayer: alice.PublicKey,
Instructions: ins,
RecentBlockhash: recentBlockhashResponse.Blockhash,
})
// 交易包含签名和消息
tx, err := types.NewTransaction(types.NewTransactionParam{
Message: message,
Signers: []types.Account{alice, bob},
})
if err != nil {
log.Fatalf("failed to new transaction, err: %v", err)
}
// 3、广播上链
txhash, err := c.SendTransaction(context.Background(), tx)
if err != nil {
log.Fatalf("failed to SendTransaction, err: %v", err)
}
fmt.Println("tx hash", txhash)
}

// 下面是使用sdk自带的方法
bob := types.NewAccount()
fmt.Printf("bob address=%s\n", bob.PublicKey.String())

createAccountIns2 := system.CreateAccount(system.CreateAccountParam{
From: alice.PublicKey,
New: bob.PublicKey,
Owner: common.SystemProgramID, // 你要为他创建哪个帐户就用哪个owner
Lamports: minimumBalanceForRentExemption + 1, // 最低租金+1
Space: 0,
})
ins = append(ins, createAccountIns2)

总结

  • 程序调用指令
  • 指令通过交易发送广播出去
  • 交易需要是原子的
  • 所有交易都必须签名

Solana 交易的生命周期—->不单单是solana 很多题目都会出现

  • 指令包含什么:程序id、 帐户key数组 、data
  • 交易包含什么:指令、RecentBlockhash、签名消息、fee支付者
  • 去中心化应用DAPP 客户端 组装交易(指令),
  • 交易被发送到RPC 客户端
  • RPC客户端把所有交易转发给投票验证者
  • 验证者将实际使用solana运行时来启用,运行时实际执行每个交易内的每个指令
  • 每个指令将调用一个特定程序来实际执行该程序代码试图做的事情,这个程序只是增加一个计数器并递增1

复杂指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
func CreateComplexTx() {

c := NewClient()
alice, err := types.AccountFromBase58(Piv)
if err != nil {
fmt.Printf("err=%v", err)
}
fmt.Printf("alice=%v\n", alice.PublicKey.String())
// 查询下地址的当前余额
balances, err := c.GetBalance(context.Background(), alice.PublicKey.String())
if err != nil {
fmt.Printf("get balances err = %v", err)
}
fmt.Printf("balances = %v\n", balances)
// 2、组装交易
// 2.1 拿recentBlockhashResponse
recentBlockhashResponse, err := c.GetLatestBlockhash(context.Background())
if err != nil {
log.Fatalf("failed to get recent blockhash, err: %v", err)
}

// 最小转账金额
minimumBalanceForRentExemption, err := c.GetMinimumBalanceForRentExemption(context.Background(), 0)
fmt.Printf("minimumBalanceForRentExemption = %v\n", minimumBalanceForRentExemption)
// 2.2 定义Instruction然后填充它
ins := make([]types.Instruction, 0, 4)
// 交易分成2个大部份 Transactions 和 Instructions 指令,Instructions可以有多个
// 追加优先费
ComputerUnitPrice := uint64(2000000) //一个计算单元的价格
ComputerUnitLimit := uint32(2000000) //计算单元的限制
ins = append(ins, compute_budget.SetComputeUnitPrice(compute_budget.SetComputeUnitPriceParam{
MicroLamports: ComputerUnitPrice,
}))

ins = append(ins, compute_budget.SetComputeUnitLimit(compute_budget.SetComputeUnitLimitParam{
Units: ComputerUnitLimit,
}))

// DOINGS 我在做的事情是创建一个地址并为其在链上创建一个账号,参考createAccount方法
// 创建测试帐户指令,生成一个钱包,先不管他的地址
bob := types.NewAccount()
fmt.Printf("bob address=%s\n", bob.PublicKey.String())
createAccountIns2 := system.CreateAccount(system.CreateAccountParam{
From: alice.PublicKey,
New: bob.PublicKey,
Owner: common.SystemProgramID,
Lamports: minimumBalanceForRentExemption + 1,
Space: 0,
})
ins = append(ins, createAccountIns2)
// 第二个指令,给静态地址转账
Cindy := common.PublicKeyFromString("2e7MJy7rh3mr7myjEtyD6Bjyyc9fYcYVwwhGhgVDbx5U") // 随便一个静态地址
ins = append(ins, system.Transfer(system.TransferParam{
From: alice.PublicKey,
To: Cindy,
Amount: 100000000,
}))
// 第三个指令,给测试帐户转账
ins = append(ins, system.Transfer(system.TransferParam{
From: alice.PublicKey,
To: bob.PublicKey,
Amount: 200000000,
}))
// 第四个指令,给静态地址转账
ins = append(ins, system.Transfer(system.TransferParam{
From: alice.PublicKey,
To: Cindy,
Amount: 300000000,
}))
// 消息包含指令、区块哈希、fee支付者
message := types.NewMessage(
types.NewMessageParam{
FeePayer: alice.PublicKey,
Instructions: ins,
RecentBlockhash: recentBlockhashResponse.Blockhash,
})
// 交易包含签名和消息
tx, err := types.NewTransaction(types.NewTransactionParam{
Message: message,
Signers: []types.Account{alice, bob},
})
if err != nil {
log.Fatalf("failed to new transaction, err: %v", err)
}
// 3、广播上链
txhash, err := c.SendTransaction(context.Background(), tx)
if err != nil {
log.Fatalf("failed to SendTransaction, err: %v", err)
}
fmt.Println("tx hash", txhash)
}

TOKEN 指令–>创建Token和元数据

createTokenWithMetadata

分成4个步骤

  1. Create a new account 创建一个新帐户,
  2. Initialize that account as a mint 初始化这个帐户为铸造方
  3. Create an associated token account 创建关联代币帐户 ATA
  4. Mint new tokens to that associated token account 将新代币铸造到关联的代币账户

我们先完成前面3个步骤来创建token和元数据,3个指令

createMintAccountInstruction,

initializeMintInstruction,

createMetadataInstruction,

见代码

第四步就是mint to 铸造一定数量的token发送给某个接收者地址

见代码

更改代币元数据

见代码

总结铸造流程:

  1. 创建一个帐户,system.CreateAccount()方法
  2. 为帐户初始化为一个铸币帐户Mint token.InitializeMint()方法
  3. 为该帐户初始名称并创建元数据帐户地址(PDA派生那一步),common.FindProgramAddress()加上token_metadata.CreateMetadataAccountV3()方法
  4. create an associated token account for the user’s wallet为用户的钱包创建关联的代币账户 ·associated_token_account.Create()方法
  5. mint a token to the user’s associated token account向用户关联的代币账户铸造代币 token.MintTo()方法

NFT

NFT就是一个splat Token 是一个具有独特属性的非同质化代币

特性

  1. Are SPL Tokens 是token
  2. 小数精度为0
  3. 总供应量为1
  4. 高度可定制的元数据如图像等各种属性

还有两个概念,主板和集合

调用合约

参考交易https://solscan.io/tx/4kEqM9WYQnud3G2UjiNgRktQKGkRu3B4PSuLepSrqYubU3Ha5J88PGmWNnt85MKwDuCyrc2q5odU8nWRNnUFVtNw?cluster=devnet

创建池子的交易:

https://solscan.io/tx/4kEqM9WYQnud3G2UjiNgRktQKGkRu3B4PSuLepSrqYubU3Ha5J88PGmWNnt85MKwDuCyrc2q5odU8nWRNnUFVtNw?cluster=devnet

3aPmA4hTkMQMFfF5cpktFSK6rniJHoL2DdmqVi1q3SoLTNXzisK4FiW6XJcD1tftygq9n1vMyjBLBrP6gymWapeK

  • 得到的代币可容忍的数量差,超出容忍度,超出

AMM

CLMM

CPMM


07-5SOL钱包开发流程
http://example.com/2024/11/22/07-5SOL钱包开发流程/
作者
Wangxiaowang
发布于
2024年11月22日
许可协议