基于BodingCurve价格发现的代币

基于BondingCurve价格发现的代币

相关链接

关于抢跑问题

  • 通过设置一个最大的gas price,可以避免抢跑问题
1
2
3
4
5
6
7
8
9
10
11
12
13

contract CappedGasPrice is Ownable {
uint256 public maxGasPrice = 1 * 10**18; // Adjustable value

modifier validGasPrice() {
require(tx.gasprice <= maxGasPrice, "Transaction gas price cannot exceed maximum gas price.");
_;
}

function setMaxGasPrice(uint256 gasPrice) public onlyOwner {
maxGasPrice = gasPrice;
}
}

P2P技术(UDP)打洞

其中最终核心的就是:

端点在不同的NAT之后

假设客户端A和客户端B的地址都是内网地址,且在不同的NAT后面. A、B上运行的P2P应用程序和服务器S都使用了UDP端口1234,A和B分别初始化了 与Server的UDP通信,地址映射如图所示:

1
2
3
4
5
6
7
8
9
10
11
12
                            Server S
18.181.0.31:1234
|
|
+----------------------+----------------------+
| |
NAT A NAT B
155.99.25.11:62000 138.76.29.7:31000
| |
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234

现在假设客户端A打算与客户端B直接建立一个UDP通信会话. 如果A直接给B的公网地址138.76.29.7:31000发送UDP数据,NAT B将很可能会无视进入的 数据(除非是Full Cone NAT),因为源地址和端口与S不匹配,而最初只与S建立过会话. B往A直接发信息也类似.

假设A开始给B的公网地址发送UDP数据的同时,给服务器S发送一个中继请求,要求B开始给A的公网地址发送UDP信息. A往B的输出信息会导致NAT A打开 一个A的内网地址与与B的外网地址之间的新通讯会话, B往A亦然. 一旦新的UDP会话在两个方向都打开之后,客户端A和客户端B就能直接通讯, 而无须再通过引导服务器S了.

UDP打洞技术有许多有用的性质. 一旦一个的P2P链接建立,链接的双方都能反过来作为“引导服务器”来帮助其他中间件后的客户端进行打洞, 极大减少了服务器的负载. 应用程序不需要知道中间件具体是什么(如果有的话),因为以上的过程在没有中间件或者有多个中间件的情况下 也一样能建立通信链路.

  • 服务端: https://github.com/youngqqcn/P2P-Over-MiddleBoxes-Demo/blob/master/p2pchat/server.c

    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

    void on_message(int sock, endpoint_t from, Message msg) {
    log_debug("RECV %d bytes FROM %s: %s %s", msg.head.length,
    ep_tostring(from), strmtype(msg.head.type), msg.body);
    switch(msg.head.type) {


    case MTYPE_LOGIN: // 登录, 记录客户端的地址
    {
    if (0 == eplist_add(g_client_pool, from)) {
    log_info("%s logged in", ep_tostring(from));
    udp_send_text(sock, from, MTYPE_REPLY, "Login success!");
    } else {
    log_warn("%s failed to login", ep_tostring(from));
    udp_send_text(sock, from, MTYPE_REPLY, "Login failed");
    }
    }
    break;

    // ....

    case MTYPE_PUNCH: // UDP打洞核心逻辑
    {
    endpoint_t other = ep_fromstring(msg.body);
    log_info("punching to %s", ep_tostring(other));

    // 向目的地址发送打洞PUNCH消息, 并将源地址作为消息体,发给目的地址
    udp_send_text(sock, other, MTYPE_PUNCH, ep_tostring(from));

    // 向源地址发送一个消息, 源地址收到不会回复
    udp_send_text(sock, from, MTYPE_TEXT, "punch request sent");

    }
    break;
    case MTYPE_PING:
    udp_send_text(sock, from, MTYPE_PONG, NULL);
    break;
    case MTYPE_PONG:
    break;
    default:
    udp_send_text(sock, from, MTYPE_REPLY, "Unkown command");
    break;
    }
    }

  • 客户端:

    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

    void on_message(endpoint_t from, Message msg) {
    log_debug("RECV %d bytes FROM %s: %s %s", msg.head.length,
    ep_tostring(from), strmtype(msg.head.type), msg.body);
    // from server
    if (ep_equal(g_server, from)) {
    switch (msg.head.type) {
    case MTYPE_PUNCH: // 收到服务端的打洞请求,
    {
    endpoint_t peer = ep_fromstring(msg.body);
    log_info("%s on call, replying...", ep_tostring(peer));

    // 给源地址回复一条消息,
    udp_send_text(g_clientfd, peer, MTYPE_REPLY, NULL);
    }
    break;
    case MTYPE_REPLY:
    log_info("SERVER: %s", msg.body);
    break;
    default:
    break;
    }
    return;
    }
    // from peer
    switch (msg.head.type) {
    case MTYPE_TEXT:
    log_info("Peer(%s): %s", ep_tostring(from), msg.body);
    break;
    case MTYPE_REPLY: // UDP打洞打通了
    log_info("Peer(%s) replied, you can talk now", ep_tostring(from));
    eplist_add(g_peers, from);
    case MTYPE_PUNCH:
    /*
    * Usually we can't recevie punch request from other peer directly,
    * but it could happen when it come after we reply the punch request from server,
    * or there's a tunnel already.
    * */
    log_info("Peer(%s) punched", ep_tostring(from));
    udp_send_text(g_clientfd, from, MTYPE_TEXT, "I SEE YOU");
    break;
    case MTYPE_PING:
    udp_send_text(g_clientfd, from, MTYPE_PONG, NULL);
    log_info("Peer(%s) pinged", ep_tostring(from));
    default:
    break;
    }
    }

以上的代码我在本地和2台服务做了测试,成功:

1
2
3
4
5
            Server(腾讯云服务器)


ClientA(本机) ClientB(aws服务器 )

strtok源码

strtok源码分析

1
2
3
4
5
6
7
8
9
10
11
12
#include <string.h>
#include <stdio.h>

int main () {
char str[80] = "192.168.10.110:9000";
char *pszHost = strtok(str, ":");
char *pszPort = strtok(NULL, ":");
printf("%s\n", pszHost);
printf("%s\n", pszPort);
return(0);
}

输出

1
2
192.168.10.110
9000
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
/* Parse S into tokens separated by characters in DELIM.
If S is NULL, the last string strtok() was called with is
used. For example:
char s[] = "-abc-=-def";
x = strtok(s, "-"); // x = "abc"
x = strtok(NULL, "-="); // x = "def"
x = strtok(NULL, "="); // x = NULL
// s = "abc\0=-def\0"
*/
char *
strtok (char *s, const char *delim)
{
static char *olds; // 保留上一次的位置
return __strtok_r (s, delim, &olds);
}



/* Parse S into tokens separated by characters in DELIM.
If S is NULL, the saved pointer in SAVE_PTR is used as
the next starting point. For example:
char s[] = "-abc-=-def";
char *sp;
x = strtok_r(s, "-", &sp); // x = "abc", sp = "=-def"
x = strtok_r(NULL, "-=", &sp); // x = "def", sp = NULL
x = strtok_r(NULL, "=", &sp); // x = NULL
// s = "abc\0-def\0"
*/
char *
__strtok_r (char *s, const char *delim, char **save_ptr)
{
char *end;
if (s == NULL)
s = *save_ptr;
if (*s == '\0')
{
*save_ptr = s;
return NULL;
}
/* Scan leading delimiters. */
s += strspn (s, delim);
if (*s == '\0')
{
*save_ptr = s;
return NULL;
}
/* Find the end of the token. */
end = s + strcspn (s, delim);
if (*end == '\0')
{
*save_ptr = end;
return s;
}
/* Terminate the token and make *SAVE_PTR point past it. */
*end = '\0'; // 设置结束符
*save_ptr = end + 1; // 指针移动到下一个位置
return s;
}

react基础

react中文教程

React环境搭建

antd使用

https://ant-design.antgroup.com/components/overview-cn

  • 全局安装 npm install antd-init -g
  • antd-init
  • npm install antd –save

React

在你的组件显示在屏幕上之前,它们必须由 React 进行渲染。理解这个过程中的步骤有助于你思考你的代码如何执行并解释其行为。

想象一下,你的组件是厨房里的厨师,用食材制作出美味的菜肴。在这个场景中,React 是服务员,负责提出顾客的要求,并给顾客上菜。这个请求和服务 UI 的过程有三个步骤:

  • 触发渲染(将食客的订单送到厨房)
  • 渲染组件(在厨房准备订单)
  • 提交到 DOM(将订单送到桌前)

作为快照的状态

  • 与普通 JavaScript 变量不同,React 状态的行为更像一个快照。设置它并不改变你已有的状态变量,而是触发一次重新渲染。这在一开始可能会让人感到惊讶!
1
2
3
4
5
6
7
console.log(score);  // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0

更新数组

避免使用 (会改变原始数组) 推荐使用 (会返回一个新数组)
添加元素 push,unshift concat,[…arr] 展开语法(例子)
删除元素 pop,shift,splice filter,slice(例子)
替换元素 splice,arr[i] = … 赋值 map(例子)
排序 reverse,sort 先将数组复制一份(例子)

Context使用场景

ref 和 state 的不同之处

ref state
useRef(initialValue)返回 { current: initialValue } useState(initialValue) 返回 state 变量的当前值和一个 state 设置函数 ( [value, setValue])
更改时不会触发重新渲染 更改时触发重新渲染。
可变 —— 你可以在渲染过程之外修改和更新 current 的值。 “不可变” —— 你必须使用 state 设置函数来修改 state 变量,从而排队重新渲染。
你不应在渲染期间读取(或写入) current 值。 你可以随时读取 state。但是,每次渲染都有自己不变的 state 快照。

Effect

1
2
3
4
5
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1);
});

  • Copyrights © 2021-2025 youngqqcn

请我喝杯咖啡吧~