推送 RPC
RPC 节点允许连接通过 WebSocket 进行长连接。
通过在该长连接上发送订阅请求, RPC 节点会将相关事件在长连接上推送过来。
当前订阅主要分为:
- accountSubscribe : 订阅 Account 的变化,比如 lamports
- logsSubscribe : 订阅交易的日志
- programSubscribe : 订阅合约 Account 的变化
- signatureSubscribe : 订阅签名状态变化
- slotSubscribe : 订阅 slot 的变化
每个事件,还有对应的取消订阅 Unsubscribe 动作,将上面的 Subscribe 替换成 Unsubscribe 即可。
这里我们通过 wscat 命令行工具来模拟 wss 客户端。
首先安装工具:
npm install -g ws wscat
然后建立连接:
wscat -c wss://api.devnet.solana.com
1. 订阅账号变化
这里的 Account 就是每个地址的 Account 元数据,主要变化的就是 data 部分和 lamports 部分。
比如,我们要订阅我们的账号余额的变化。
{
"jsonrpc": "2.0",
"id": 1,
"method": "accountSubscribe",
"params": [
"CnjrCefFBHmWnKcwH5T8DFUQuVEmUJwfBL3Goqj6YhKw",
{
"encoding": "jsonParsed",
"commitment": "finalized"
}
]
}
这里订阅对账号的变化的事件,我们通过 wscat 来模拟:
wscat -c wss://api.devnet.solana.com
Connected (press CTRL+C to quit)
> {"jsonrpc":"2.0","id":1,"method":"accountSubscribe","params":["EZhhUANUMKsRhRMArczio1kLc9axefTUAh5xofGX35AK",{"encoding":"jsonParsed","commitment":"finalized"}]}
< {"jsonrpc":"2.0","result":3283925,"id":1}
然后,我们在另外一个终端里面进行转账:
solana transfer --allow-unfunded-recipient CZmVK1DymrSVWHiQCGXx6VG5zgHVrh5J1P514jHKRDxA 0.01
接着我们注意观察上面的 wscat:
Connected (press CTRL+C to quit)
> {"jsonrpc":"2.0","id":1,"method":"accountSubscribe","params":["CnjrCefFBHmWnKcwH5T8DFUQuVEmUJwfBL3Goqj6YhKw",{"encoding":"jsonParsed","commitment":"finalized"}]}
< {"jsonrpc":"2.0","result":3283925,"id":1}
< {"jsonrpc":"2.0","method":"accountNotification","params":{"result":{"context":{"slot":209127027},"value":{"lamports":989995000,"data":["","base64"],"owner":"11111111111111111111111111111111","executable":false,"rentEpoch":0,"space":0}},"subscription":3283925}}
我们会发现,一段时间后,也就是到达了 “finalized”状态后,就会将修改过后的 Account 信息推送过来:
{
"lamports": 989995000,
"data": [
"",
"base64"
],
"owner": "11111111111111111111111111111111",
"executable": false,
"rentEpoch": 0,
"space": 0
}
可以看到这里余额发生了变化。
2. 订阅日志
订阅日志可能是做应用最常见到的,任何在 log 里面打印了相关事件的交易都会被通知。
{
"jsonrpc": "2.0",
"id": 1,
"method": "logsSubscribe",
"params": [
{
"mentions": [ "CdJp6W7S8muM85UXq7u2P42ryytDacqEo8JgoHENSiUi" ]
},
{
"commitment": "finalized"
}
]
}
这里 mentions 来指定,通知了哪个程序或者账号的地址。
比如这里我们订阅我们的一个ATA 的账号:
wscat -c wss://api.devnet.solana.com
Connected (press CTRL+C to quit)
> {"jsonrpc":"2.0","id":1,"method":"logsSubscribe","params":[{"mentions":["CdJp6W7S8muM85UXq7u2P42ryytDacqEo8JgoHENSiUi"]},{"commitment":"finalized"}]}
< {"jsonrpc":"2.0","result":610540,"id":1}
然后我们给这个地址做 mint 增加余额:
spl-token mint 7dyTPp6Jd1nWWyz3y7CXqdSG86yFpVF7u45ARKnqDhRF 1000000000
Minting 1000000000 tokens
Token: 7dyTPp6Jd1nWWyz3y7CXqdSG86yFpVF7u45ARKnqDhRF
Recipient: CdJp6W7S8muM85UXq7u2P42ryytDacqEo8JgoHENSiUi
Signature: 5NVHNccPo4ADxnHZjVSYZzxk3fuZfZvuLP6MwkhSNBbQRNcGfC2gwScz24XYictZuqaMKFEcmsXuHV4WZDiFUD3r
可以在事件通知中看到:
wscat -c wss://api.devnet.solana.com
Connected (press CTRL+C to quit)
> {"jsonrpc":"2.0","id":1,"method":"logsSubscribe","params":[{"mentions":["CdJp6W7S8muM85UXq7u2P42ryytDacqEo8JgoHENSiUi"]},{"commitment":"finalized"}]}
< {"jsonrpc":"2.0","result":610540,"id":1}
< {"jsonrpc":"2.0","method":"logsNotification","params":{"result":{"context":{"slot":209131722},"value":{"signature":"5NVHNccPo4ADxnHZjVSYZzxk3fuZfZvuLP6MwkhSNBbQRNcGfC2gwScz24XYictZuqaMKFEcmsXuHV4WZDiFUD3r","err":null,"logs":["Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]","Program log: Instruction: MintToChecked","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4498 of 200000 compute units","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success"]}},"subscription":610540}}
这里有个 "MintToChecked" 指令。
3. 订阅合约所属的账号事件
比如我们希望知道所有 Token 合约管理的账号的余额变化是,我们可以通过订阅合约管理的账号事件来发现:
{
"jsonrpc": "2.0",
"id": 1,
"method": "programSubscribe",
"params": [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{
"encoding": "jsonParsed"
}
]
}
对应的命令:
wscat -c wss://api.devnet.solana.com
Connected (press CTRL+C to quit)
> {"jsonrpc":"2.0","id":1,"method":"programSubscribe","params":["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",{"encoding":"jsonParsed"}]}
< {"jsonrpc":"2.0","result":142408,"id":1}
< {"jsonrpc":"2.0","method":"programNotification","params":{"result":{"context":{"slot":209131042},"value":{"pubkey":"GGUY45VyYy9j7vFdHRP3ecyMYhFCfrCBpVQaUxoEtHfv","account":{"lamports":2039280,"data":{"program":"spl-token","parsed":{"info":{"isNative":false,"mint":"GCBnu9k28isstJjCcYoZZcyTkMh5cXTsk7abpgWJesQT","owner":"AV1JYHgShqNdbza84sLi7Hgbtfgd1hn9mNMgez4twBuG","state":"initialized","tokenAmount":{"amount":"0","decimals":9,"uiAmount":0.0,"uiAmountString":"0"}},"type":"account"},"space":165},"owner":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","executable":false,"rentEpoch":0,"space":165}}},"subscription":142408}}
< {"jsonrpc":"2.0","method":"programNotification","params":{"result":{"context":{"slot":209131042},"value":{"pubkey":"GGUXUncym8riA1izYZnBWspYL1k4rVBnLuZ3KbUnc6WG","account":{"lamports":2039280,"data":{"program":"spl-token","parsed":{"info":{"isNative":false,"mint":"GCBnu9k28isstJjCcYoZZcyTkMh5cXTsk7abpgWJesQT","owner":"2E7BD9ibbHinwohM4pLFsjdFYq1S2o4wqKmfaQXXg8Dr","state":"initialized","tokenAmount":{"amount":"0","decimals":9,"uiAmount":0.0,"uiAmountString":"0"}},"type":"account"},"space":165},"owner":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","executable":false,"rentEpoch":0,"space":165}}},"subscription":142408}}
< {"jsonrpc":"2.0","method":"programNotification","params":{"result":{"context":{"slot":209131042},"value":{"pubkey":"GGUZyCzhCKEFZMdf8mDfUU4L1tr4q2xh3FHRWpRM8cPB","account":{"lamports":2039280,"data":{"program":"spl-token","parsed":{"info":{"isNative":false,"mint":"GCBnu9k28isstJjCcYoZZcyTkMh5cXTsk7abpgWJesQT","owner":"7PydWu5QtMcbdj7qgdgn42Rwp247GFf3e2pQ5fQ8LRGY","state":"initialized","tokenAmount":{"amount":"0","decimals":9,"uiAmount":0.0,"uiAmountString":"0"}},"type":"account"},"space":165},"owner":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","executable":false,"rentEpoch":0,"space":165}}},"subscription":142408}}
< {"jsonrpc":"2.0","method":"programNotification","params":{"result":{"context":{"slot":209131042},"value":{"pubkey":"GGUZzsex1ybU4V1duGetLHqFc7zz74jPbLp6rvNoScrR","account":{"lamports":2039280,"data":{"program":"spl-token","parsed":{"info":{"isNative":false,"mint":"GCBnu9k28isstJjCcYoZZcyTkMh5cXTsk7abpgWJesQT","owner":"2E7BD9ibbHinwohM4pLFsjdFYq1S2o4wqKmfaQXXg8Dr","state":"initialized","tokenAmount":{"amount":"0","decimals":9,"uiAmount":0.0,"uiAmountString":"0"}},"type":"account"},"space":165},"owner":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","executable":false,"rentEpoch":0,"space":165}}},"subscription":142408}}
< {"jsonrpc":"2.0","method":"programNotification","params":{"result":{"context":{"slot":209131042},"value":{"pubkey":"GGUV4mWenyQGyVVCNV3xPjmioJoMCCYSPFxFzFB3AmBt","account":{"lamports":2039280,"data":{"program":"spl-token","parsed":{"info":{"isNative":false,"mint":"GCBnu9k28isstJjCcYoZZcyTkMh5cXTsk7abpgWJesQT","owner":"2E7BD9ibbHinwohM4pLFsjdFYq1S2o4wqKmfaQXXg8Dr","state":"initialized","tokenAmount":{"amount":"0","decimals":9,"uiAmount":0.0,"uiAmountString":"0"}},"type":"account"},"space":165},"owner":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","executable":false,"rentEpoch":0,"space":165}}},"subscription":142408}}
这里里面就可以看到。有很多的 SPL-Token 账号都在变化。并且因为我们加了 "jsonParsed",所以这里 SPL-Token 的内容也展示出来了。
4. 订阅交易状态
比如,我们希望在我们发起交易后,第一时间知道交易的确定状态,我们可以通过订阅该事件来实现:
{
"jsonrpc": "2.0",
"id": 1,
"method": "signatureSubscribe",
"params": [
"BfQAbgqQZMfsxFHwh6Hve8yGb843QfZcYtD2j2nN3K1hLHZrQjzdwG9uWgNkGXs4tBNVLE3JAzvNLtwJBt3zDsN",
{
"commitment": "finalized",
"enableReceivedNotification": false
}
]
}
我们再次发起一笔转账交易:
solana transfer --allow-unfunded-recipient CZmVK1DymrSVWHiQCGXx6VG5zgHVrh5J1P514jHKRDxA 0.01
Signature: BfQAbgqQZMfsxFHwh6Hve8yGb843QfZcYtD2j2nN3K1hLHZrQjzdwG9uWgNkGXs4tBNVLE3JAzvNLtwJBt3zDsN
然后,在另外一个终端,迅速建立 wscat 连接,并订阅该事件:
wscat -c wss://api.devnet.solana.com
Connected (press CTRL+C to quit)
> {"jsonrpc":"2.0","id":1,"method":"signatureSubscribe","params":["BfQAbgqQZMfsxFHwh6Hve8yGb843QfZcYtD2j2nN3K1hLHZrQjzdwG9uWgNkGXs4tBNVLE3JAzvNLtwJBt3zDsN",{"commitment":"finalized","enableReceivedNotification":false}]}
< {"jsonrpc":"2.0","result":3285176,"id":1}
< {"jsonrpc":"2.0","method":"signatureNotification","params":{"result":{"context":{"slot":209127740},"value":{"err":null}},"subscription":3285176}}
当到达 "finalized" 状态时,会通知我们,该交易已经成功。