逆向分析pump.fun的BondingCurve算法

找到 div id, 或者 用报错文本 进行全局搜索

全局搜索

可以看到 onChange事件处理函数:

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
onChange:  es ? e=>{
if (et)
return;
let t = parseFloat(e.target.value);
if (isNaN(t)) {
g(""),
f("");
return
}
g(t),
f(I(new ex.BN(Math.floor(1e9 * t)), !0).toNumber() / 1e6)
}
: e=>{
let t;
if (et)
return;
let n = parseFloat(e.target.value);
if (isNaN(n)) {
f(""),
g("");
return
}
f(n);
let s = new ex.BN(1e6 * n);
t = u ? I(s, !1) : A(s),
g((0,
p.s)(t))
}
})

可知, 函数I正是算法函数的实现, 接下来,需要定位 I函数的位置,

我们在 onChange函数中打两个断点, 然后在输入框输入数量,触发执行到断点处

此时,将鼠标放置在I上就可以查看函数的位置:

或者,直接在调试窗口的下方控制台,直接输入 I, 然后双击输出, 也可以查看I的定义,

至此,我们已经找到pump.fun的bonding curve的算法实现函数:

根据前面的分析,

  • 参数 n 是个bool值, 表示的是按照sol还是按照token买入
  • 参数 e 是数量

因此,
其中 i 是 bigint库

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
 // 买入
function x(e,n)=>{
let s, a;
if (e.eq(new i.BN(0)) || !t)
return new i.BN(0);
if (n) {
// 按照 sol数量买入
let n = t.virtualSolReserves.mul(t.virtualTokenReserves)
, r = t.virtualSolReserves.add(e)
, o = n.div(r).add(new i.BN(1));
a = t.virtualTokenReserves.sub(o),
a = i.BN.min(a, t.realTokenReserves),
s = e
} else
// 按照 token数量买入
s = (e = i.BN.min(e, t.realTokenReserves)).mul(t.virtualSolReserves).div(t.virtualTokenReserves.sub(e)).add(new i.BN(1)),
a = e;
let r = _(s); // 手续费
return n ? a : s.add(r) //SOL加上手续费
}

// 卖出
sellQuote: e=>{
if (e.eq(new i.BN(0)) || !t)
return new i.BN(0);
let n = e.mul(t.virtualSolReserves).div(t.virtualTokenReserves.add(e))
, s = _(n); // 手续费
return n.sub(s) // 扣除手续费
}
变量 变量全称 类型 说明(统一使用最小单位) 初始值
Tv virtualTokenReserves u64 虚拟token库存量 1073000000 * 10^6
Sv virtualSolReserves u64 虚拟SOL的库存量 30 * 10^9
Tr realTokenReserves u64 真实的token库存量 793100000 * 10^6
Sr realSolReserves u64 真实的SOL的库存量 0 * 10^9

18_Solana高级交易Durable_Nonce

官方文档:

本质问题: 如何避免双花?

Recent Blockhash 做了时间戳,也充当了唯一标识(类似ETH的nonce)的作用, 防止双花

有了 Recent Blockhash 为什么还需要 Durable Nonce?

Recent Blockhash 的窗口是 150个区块(约 150 * 0.4 = 60s), 因此,签名之后的交易必须在一分钟内被提交执行,否则交易就会过期。

几个特殊场景:

  • 大批量交易, 不想因为blockhash重复而失败 ?
  • 多重签名交易?
  • 离线签名?

因此,就需要 Durable Nonce 方案, nonce 是 32字节, 其作用就是确保交易的唯一

Polymarket技术分析

Polymarket技术分析

polymarket介绍:

关键概念:

  • 预测事件: 二元事件,即结果是发生和不发生

    • 例如:
      • 特朗普赢得2024大选,其结果是 YesNo
      • 谁将赢得2024大选? 有多个候选人,但是每个候选人最后的结果只有2种: YesNo
  • shares: 股份, 即持有某个预测的凭证, 实际上是ERC1155代币, 可以理解为股份

  • 用户可以买入或卖出某个事件的 Yes的shares 或 No的shares

  • 关于价格: 0-1美金的价格, 对应当前市场对于该事件的概率(0-1)预测, polymarket使用美分¢ , 1美元 = 100美分

    • 例如: 特朗普赢得2024大选的事件
      • 目前 Yes的Shares的市场价格是 45¢ , 代表目前市场对于特朗普赢得大选的预测的概率是 45%
      • 目前 No的shares的市场价格是 56¢, 即与 Yes的概率相反, 是56%
  • 可以使用 市价限价进行买入卖出

    • 例如:
      • 市价买入, 则买入价格是 卖1的价格;
      • 市价卖出,则卖出价格是 买1的价格
    • 例如:
      • 当前某事件的Yes市价是 45¢, 你挂单 40¢的买入单,则需要等待有40¢卖出单(可以部分成交)才能撮合
  • 关于清算: 如果事件发生了,那么每股清算价格为1$, 即 100¢; 如果事件没有发生,则每股清算价格为 0
    • 例如,你觉得特朗普会赢得2024大选, 那么你按照Yes市价 45¢买入10股,即 450¢, 即4.5$ , 如果特朗普赢了大选, 每股的价格是 100¢, 即1$,那么,你最终将获得是: 100 * 10 = 1000¢ , 那么你的利润是 1000 - 450 = 550, 即 5.5$; 如果特朗普输了大选, 则你损失全部的 450¢ , 即4.5$.
  • 是否可以下注高赔率的事件(加杠杆)?

    • 需要有对手单,polymarket并没有提供杠杆玩法,即polymarket不坐庄,用户实际上是和其他用户做对手单

    • 例如:

      • 你非常不看好特朗普, 觉得他赢得大选概率为0,那么你用 的价格挂一个买入 Yes的单,数量为1000股, 那么, 理论上说,如果事件没有发生,你可以获得 1000$, 利润是 990$, 相当于赔率是 1000倍
      • 但是,你的这笔挂单需要有一个对手单, 即市场上有一另外一个赌徒,他非常看好特朗普,觉得特朗普100%会赢得大选
    • 为了方便起见,使用(BUY, YES, 1) * 1000 表示按照每股1美分的价格买入YES股的1000股

    • 上面的例子: 形成对手单,有2种方式

      • 第一种: 直接方式
        • (BUY, YES, 1) * 1000 的直接对手单是 (SELL, YES, 1) * 1000, 即之前有一个已经有 1000股 YES, 他卖给你
      • 第二种: 间接方式, 因为 YESNO 是共享订单簿的
        • (BUY, YES, 1) * 1000(SELL, NO, 99) * 1000 等价, 可以理解逆否命题
        • (SELL, NO, 99) * 1000 的对手单是 (BUY, NO, 99) * 1000,
        • 因此, (BUY, NO, 99) * 1000 也是 (BUY, YES, 1) * 1000的间接对手单,
        • 可以理解为, 市场上有一个人, 按照 99美分的价格买入1000股YES, 和你形成对手单

ERC20高级充币归集技术

核心技术概括:

  • 使用 CREATE2派生确定的充币地址(合约)
  • 归集时在合约中 使用相同的 salt 和 hash, 创建充币地址(合约)
    • 在合约中执行 ERC20的 approve,授权本合约
    • 调用 selfdestruct销毁合约
  • 转移使用transferFrom转移充币地址中的ERC20代币

实际案例

技术点剖析:

  • 充币地址(接收地址)是一个”临时”合约地址
  • “临时”地址可以派生出来
  • 且,”归集”合约可以操作 “临时”地址进行 approve操作
  • approve完成后即自毁(selfdestruct)了临时合约地址

合约代码分析:

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
// Function that bridges taking amount from the t2bAddress where the user funds are parked.
function bridgeERC20(
uint256 fees,
uint256 nonce,
bytes calldata bridgeData,
bytes calldata signature
) external {
// recovering signer.
address recoveredSigner = ECDSA.recover(
keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
keccak256(
abi.encode(
address(this),
nonce,
block.chainid, // uint256
fees,
bridgeData
)
)
)
),
signature
);

if (signerAddress != recoveredSigner) revert SignerMismatch();
// nonce is used by gated roles and we don't expect nonce to reach the max value of uint256
unchecked {
if (nonce != nextNonce[signerAddress]++) revert InvalidNonce();
}

if (bridgeVerifiers[uint32(bytes4(bridgeData[0:4]))] == address(0))
revert UnsupportedBridge();

// 解析数据
(bool parseSuccess, bytes memory parsedData) = bridgeVerifiers[
uint32(bytes4(bridgeData[0:4]))
].call(bridgeData[4:bridgeData.length - 1]);

if (!parseSuccess) revert VerificationCallFailed();

// 解析数据
IT2BRequest.T2BRequest memory t2bRequest = abi.decode(
parsedData,
(IT2BRequest.T2BRequest)
);

// 获取派生地址
address t2bAddress = getAddressFor(
t2bRequest.recipient,
t2bRequest.toChainId
);

// 判断 allowance
if (
ERC20(t2bRequest.token).allowance(t2bAddress, address(this)) <
t2bRequest.amount
) {
// 计算salt
bytes32 uniqueSalt = keccak256(
abi.encode(t2bRequest.recipient, t2bRequest.toChainId)
);

// 调用 CREATE2 创建临时地址
new T2BApproval{salt: uniqueSalt}(address(this));
}

// 将派生地址的 ERC20代币转移
ERC20(t2bRequest.token).safeTransferFrom(
t2bAddress,
address(this),
t2bRequest.amount + fees
);

//... 其他代码, 略
}
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

// 部署派生地址
function deployApprovalContract(
address receiver,
uint256 toChainId
) public returns (address approvalAddress) {
bytes32 uniqueSalt = keccak256(abi.encode(receiver, toChainId));
approvalAddress = address(new T2BApproval{salt: uniqueSalt}(address(this)));
}

// 获取派生地址
function getAddressFor(
address receiver,
uint256 toChainId
) public view returns (address) {
bytes32 salt = keccak256(abi.encode(receiver, toChainId));
return
address(
uint160(
uint256(
keccak256(

// 可以看下文的 CreateAddress2的实现
abi.encodePacked(
bytes1(0xff), // 固定的
address(this), // 本合约地址
salt, // salt

// 合约代码的 hash
keccak256(
abi.encodePacked(
type(T2BApproval).creationCode, // 合约代码
abi.encode(address(this)) // 合约
)
)
)
)
)
)
);
}
  • T2BApproval 派生地址合约
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
contract T2BApproval {
using SafeTransferLib for ERC20;

error ZeroAddress();
error InvalidTokenAddress();


// Constructor
constructor(address _t2bRouter) {
// Set T2b Router.
IT2BRouter t2bRouter = IT2BRouter(_t2bRouter);

// Set Max Approvals for supported tokens.
uint256 tokenIndex = 0;
while (t2bRouter.supportedTokens(tokenIndex) != address(0)) {

// 进行 approve操作
ERC20(t2bRouter.supportedTokens(tokenIndex)).safeApprove(
address(t2bRouter),
type(uint256).max
);
unchecked {
++tokenIndex;
}
}

// 销毁
selfdestruct(payable(msg.sender));
}
}

安全性:

  • new T2BApproval{salt: uniqueSalt}(address(this));
    • CREATE2 生成的地址是基于部署者地址salt合约字节码的哈希计算的。
1
2
3
//  scope.Contract 是本合约地址
// input 合约代码
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, &endowment, &salt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Create2 creates a new contract using code as deployment code.
//
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int)
(ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {

// 合约代码的hash
codeAndHash := &codeAndHash{code: code}


contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
}

生成地址

1
2
3
4
5
// CreateAddress2 creates an ethereum address given the address bytes, initial
// contract code hash and a salt.
func CreateAddress2(b common.Address, salt [32]byte, inithash []byte) common.Address {
return common.BytesToAddress(Keccak256([]byte{0xff}, b.Bytes(), salt[:], inithash)[12:])
}

程序员如何把控自己的职业

https://coolshell.cn/articles/20977.html

  • 第一,如果想要把控技术,应对这个世界的一些变化,需要大致知道这个世界的一些规律和发展趋势,另外还得认识自己,自己到底适合做什么?在这个趋势和规律下属于自己的发挥领域到底是什么?这是我们每个人都需要了解的。

  • 第二,打牢基础,以不变应万变,不管世界怎样变化,我都能很快适应它。基础的重要程度对于你能够飞多高是相当有影响的,懂原理的人比不懂原理的人能做出来的事情或是能解决的问题完全是两个层级的。

  • 第三,提升成长的效率,因为现在社会的节奏实在太快了,比二十年前快得太多,技术层出不穷,所以我们的成长也要更有效率。效率并不单指的快,效率是怎么样更有效,是有用功除以总功,怎么学到更有效的东西,或者怎么更有效学习,是我们需要掌握的另一关键。


打好基础

  • 变化都是表面的东西,内在的东西其实并没有太多的变化。
  • 不懂原理,不懂科学方法,你就不可能成长上去的,

技术的基础,我会把其它成四类:

  • 程序语言:语言的原理,类库的实现,编程技术(并发、异步等),编程范式,设计模式……
  • 系统原理:计算机系统,操作系统,网络协议,数据库原理……
  • 中间件:消息队列,缓存系统,网关代理,调度系统 ……
  • 理论知识:算法和数据结构,数据库范式,网络七层模型,分布式系统……

学习效率

  • 挑选信息源
  • 注重基础和原理
  • 使用知识图谱
  • 举一反三
  • 总结归纳
  • 实践和坚持
  • Copyrights © 2021-2024 youngqqcn

请我喝杯咖啡吧~