普通视图
摩根资管的Kelly警告:美联储降息恐损害股市和债市
黑石集团拟以约10亿美元收购希尔托普发电厂
美联储本周降息25个基点的概率为95.9%
泰森食品计划年底前在美国产品中停用高果糖浆
我国对东盟进出口连续18个月保持增长
福特将把全球总部迁至3英里外的新园区,系70年来首次搬迁
美国云服务提供商CoreWeave与英伟达签署63亿美元订单
美国周二开始对日本汽车征收15%关税
OpenAI为Codex升级,推出新版GPT-5
中美就妥善解决TikTok问题达成基本框架共识
美股三大指数集体收涨,谷歌市值超过3万亿美元
实现最大异步并发执行队列
题目说明
日常开发中遇到异步并发执行时我们通常会使用 Promise.All([])
,但是如果这个并发数量很大(如超过100)那么我们考虑到服务器的并发压力就需要设置一个最大并发数。
这也是一个初/中级的热门面试题,本文就详细介绍如何用各种姿势来实现 最大异步并发执行队列
。
/**
* 最大异步并发执行队列
* tasks 任务列表
* maxConcurrency 最大并发数
* @returns {Promise<void>}
*/
async function maxAsyncConcurrency(
tasks: Array<() => Promise<void>>,
maxConcurrency: number,
) {
// 实现这个函数
}
测试代码
const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const waitLog = async (ms, text) => {
await wait(ms);
console.log(text);
};
const main = async () => {
await maxAsyncConcurrencyRecursion(
[
() => waitLog(1000, 1),
() => waitLog(1000, 2),
() => waitLog(1000, 3),
() => waitLog(1000, 4),
() => waitLog(1000, 5),
() => waitLog(1000, 6),
() => waitLog(1000, 7),
() => waitLog(1000, 8),
() => waitLog(1000, 9),
],
3,
);
}
main();
思路1:递归实现(最好理解)
通过递归方式实现,把每个并发当成一个运行管道,每个管道实现为一个运行任务的异步函数,函数中完成一个任务就从队列里取下一个任务继续执行,直到清空队列即可。
async function maxAsyncConcurrencyRecursion(tasks, maxConcurrency) {
const queue = [...tasks];
// 运行管道
const pipeRunFn = async (fn) => {
await fn();
if (queue.length > 0) {
const nextFn = queue.shift();
await pipeRunFn(nextFn);
}
};
// 最大运行管道
const pipes = queue.splice(0, maxConcurrency);
await Promise.all(pipes.map(pipeRunFn));
}
思路2:非递归实现
将思路1中的管道异步函数递归切换成
while
循环条件来实现。
async function maxAsyncConcurrency(fns, max) {
const queue = [...fns];
let active = 0;
while(queue.length) {
if (active >= max) {
await wait(100); // 如果并发已经达到最大,就等会再进入while循环继续轮询
continue;
}
const fn = queue.shift();
active++;
fn().finally(() => {
active--;
});
}
}
更加贴合实践的用法,面向对象象实现流式新增任务
题目
class RequestQueue {
private maxConcurrent: number; // 最大并发数量
private queue: Array<() => Promise<void>> = []; // 存储任务队列
constructor(maxConcurrent: number) {
this.maxConcurrent = maxConcurrent;
}
/** 添加任务 */
public addTask(task: () => Promise<void>) {}
}
测试代码
const main = async () => {
const reqQueue = new RequestQueue(3);
reqQueue.addTask(() => waitLog(1000, 1))
await wait(100);
reqQueue.addTask(() => waitLog(1000, 2))
await wait(100);
reqQueue.addTask(() => waitLog(1000, 3))
await wait(100);
reqQueue.addTask(() => waitLog(1000, 4))
await wait(2000);
reqQueue.addTask(() => waitLog(1000, 5))
await wait(100);
reqQueue.addTask(() => waitLog(1000, 6))
await wait(100);
reqQueue.addTask(() => waitLog(1000, 7))
await wait(100);
reqQueue.addTask(() => waitLog(1000, 8))
await wait(100);
reqQueue.addTask(() => waitLog(1000, 9))
}
main();
递归实现
流式增加任务,而不是一开始就拿到全量的任务列表。新增任务时自动触发并发执行
class RequestQueueRecursion {
private maxConcurrent: number; // 最大并发数量
private queue: Array<() => Promise<void>> = []; // 存储任务队列
private active: number = 0; // 当前正在运行的任务计数
constructor(maxConcurrent: number) {
this.maxConcurrent = maxConcurrent;
}
/** 添加一个任务到队列中 */
public addTask(task: () => Promise<void>) {
this.queue.push(task);
this.execute();
}
private async execute() {
while(this.active < this.maxConcurrent && this.query.length > 0) {
this.active ++;
const fn = this.query.shift();
fn().finally(() => {
this.active--;
this.execute();
});
}
}
}
非递归实现
class RequestQueue {
private maxConcurrent: number; // 最大并发数量
private queue: Array<() => Promise<any>> = []; // 存储任务队列
private active: number = 0; // 当前正在运行的任务计数
constructor(maxConcurrent: number) {
this.maxConcurrent = maxConcurrent;
}
/** 添加一个任务到队列中 */
public addTask(task: () => Promise<any>) {
this.queue.push(task);
this.execute();
}
/** 运行管道 */
private async execute() {
const queue = this.queue;
while(queue.length > 0) {
if (this.active >= this.maxConcurrent) {
await wait(100);
continue;
}
this.active ++;
const fn = queue.shift();
fn().finally(() => {
this.active--;
});
}
}
}
鸿蒙智行「五界」新车批量现身,问界 M9 非凡大师版已在路上
根据多家媒体报道以及谍照、行业人士以及公开信息,鸿蒙智行「五界」预计将在明年内推出多达 9 款新车。
董车会整理了鸿蒙智行「五界」的现有车型以及预计将新增的车型,如下图所示。
两款超高端 SUV
在鸿蒙智行的产品规划里,明年或许会有两款定价在 70 万元左右的超豪华 SUV 亮相,分别是问界旗下的 M9 非凡大师(或加长版)以及尊界旗下的一款旗舰 SUV。
自 2023 年开始,网络上就一直不断有问界 M9 将推出非凡大师版本或加长版的传言,但直到最近,才有比较确切的信息和车辆谍照传出。
从路试车和谍照来看,问界 M9 加长版的主要造型与在售车型相似,但车长增加约 20cm 至 30cm,来到了 5.5m 的车身总长度,轴距也随之增加到 3.3 米左右。
如此之长的车身自然需要匹配更为灵活的后轮转向系统,之前就有博主曾在社交媒体上晒出过一组赛力斯专为后轮转向设计的新款后副车架,此设计将后轮转向机构前置,为后电机悬置腾出了空间。有不少人猜测这套系统正是为 M9 的加长版准备的。
新车车内将延续六座布局,重点强化豪华感和乘坐舒适性。路试车曝光的同一天,有一家疑似为问界 M9 供应商的公司曝光了一段车门板设计视频,视频中显示的车门构造与问界 M9 十分相似,但材质奢华版有较大的提升,音响网罩的设计也有所变化。
在其他配置上,问界 M9 加长版或将或首发纯电续航超过 1000km 的宁德时代固态电池,并预埋持 L4 级别的辅助驾驶硬件。
尊界新旗舰 SUV 的存在则已经得到了不少产业内人士的证实。
根据 36 氪的爆料,尊界这款旗舰 SUV 直接对标揽胜和劳斯莱斯等豪华品牌,但为了走量,定价将低于尊界 S800。
华为和江淮对这款 SUV 的市场表现信心很足,对供应链释放了全年最高销量超 4 万辆的预期,为了保障该项目的资源,江淮甚至叫停了许多乘用车、重卡、轻卡项目。
这辆车也将成为华为新一代的技术旗舰,尤其是新车的底盘将在尊界 S800 途灵龙行底盘的基础上进行升级,并同步更新辅助驾驶系统。
鸿蒙智行首款MPV
有消息称,智界品牌很早就已经着手开发基于 E0X-L 平台打造的 MPV 车型,内部代号为「EHV」。动力方面,新车计划推出纯电动和增程式两种配置,匹配由宁德时代提供的电池。
最近该车的谍照在社媒上被爆出,从谍照来看,新车在不少设计细节上都与现款的智界 R7 有极高的相似度,例如灯组位置的挖孔和两侧的竖直开孔都是智界的标志性设计元素。
新车整体的造型更偏方正一点,与智界品牌主打年轻运动的风格稍稍有点不同。
结合各方信息来看,这款智界 MPV 的原型大概率就是奇瑞原来要推的星纪元 E08,其概念车于 2024 年年底亮相,当时的奇瑞常务副总裁表示这款车是奇瑞倾注了大心血研发的豪华 MPV,期望 E08 能够成为中国的埃尔法。
根据奇瑞相关人士透露,目前星纪元 E08 项目已经暂停,现有员工大部分分流或者转岗去了智界品牌。
对于豪华 MPV 车型,智界更能撑得起来,而且同时发布两台 MPV 也会「打架」。
2025 年 8 月,华为与奇瑞签署智界品牌 2.0 合作协议,成立独立运营的「智界新能源公司」,华为全链路主导研发、制造及服务,投入超百亿资金及 5000 人研发团队,或许双方深化合作后的首款新车就将是这款 MPV。
在具体配置上,目前可以确认,新车将基于奇瑞 E0X-L 平台开发,提供纯电+增程双版本,电池由宁德时代供应,增程版搭载华为雪鸮智能增程系统。座舱内则搭载原生鸿蒙座舱、副驾零重力座椅、HUAWEI SOUND 音响系统,主打商务兼家用场景。
新车定位对标腾势 D9、极氪 009 等新能源豪华 MPV,定价或在 40 万元左右。
尚界主攻走量
尚界品牌除了已经开启预售的 H5 之外,目前还有两款新车正在研发中,分别是一辆代号 E8 的大型 SUV 和主打年轻市场,设计较为激进的运动轿跑车型。
其中,尚界新 SUV 将于 2026 年上半年亮相,其价格可能上探至 25 万元至 30 万元市场,采用华为 ADS 4.0 升级的智能辅助驾驶系统以及 800V 碳化硅高压平台。而轿跑车型将搭载华为新一代鸿蒙座舱 6.0 和华为图灵底盘技术,并有可能使用上汽已经研发多年的半固态电池技术。
尚界 H5 16.98 万元的预售价格,也得到的市场的充分肯定,预售开启后 1 小时,尚界 H5 收到了超 2.5 万辆小订。
为了保证首款车型 H5 的交付,上汽为尚界在临港建设了一座专属工厂,还盘下了上汽通用金桥工厂的部分产线,并结合后续新车,近期向供应链释放了年销 40 万辆的备料计划。
如果这些新车型全部落地,鸿蒙智行的车型矩阵将从目前的 10 款拓展到 19 款,覆盖从 15 万元至 100 万元的价位段以及从家用轿车、SUV 到硬派越野、旅行车等大部分的细分市场。
而除了鸿蒙智行五界之外,采用华为 HI 模式的车型也在逐渐增多,长安旗下深蓝、阿维塔,东风旗下岚图,广汽旗下昊铂、华望等品牌的新车基本上都采用鸿蒙座舱加乾崑智驾的方案,最近宝骏的新车型华境也将搭载华为全家桶,合资车型丰田铂智 7 也确认采用鸿蒙座舱。
对于研发实力并不强的二线车企来说,与华为合作成为其在智能化竞争中不被淘汰的快速通道。相比其他供应商,华为提供的全家桶解决方案覆盖了智能驾驶、智能座舱、三电系统和智能车云等所有核心技术领域,并具有良好的市场口碑。
或许,明年之后的新能源汽车市场会有两个新的大看点——
一是华为如何在「五界」和「HI 模式」车企之间平衡精力与资源投入,二则是其他车企要不要和华为合作,不合作又要怎么与鸿蒙智行竞争?
#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。
上帝视角看 GPU 学习笔记
上帝视角看GPU(1):图形流水线基础_哔哩哔哩_bilibili
《上帝视角看 GPU》龚大教程学习笔记。
一、图形流水线基础
首先思考一张图片是如何显示在屏幕上呢?首先需要了解一个概念帧缓存(Frame Buffer),这是内存中的一块区域,这块区域上的内容和显示器上显示的像素是一一对应的。
将帧缓存上的内容输出到屏幕上,需要通过显卡,显卡上有个输出端口连接到显示器上。
那这是最简单的需求,如果此时有一个需求:需要将图像的亮度提升 2 倍呢?也就是将每个像素的 RGB 分量都乘 2。
当然可以通过 CPU 来进行计算,但这样必然要占用大量 CPU 资源,更好的做法是加入一个 处理器(PU),这个处理器可以在每个像素上执行同样的操作;
为了适应更加灵活多变的需求,比如将图像上半部分亮度提升 2 倍,下半部分亮度提升 4 倍。我们可以在 PU 上挂上一个 **程序 **,这个程序是单入单出的,输入是像素坐标,输出的是像素 RGB 颜色,也就是 片元着色器 Pixel Shader。
好了,上边就是一个最基本的针对图像的流水线。
顺便一说,在任天堂的红白机上,就有这么一个处理器,叫做 PPU(Picture Processing Unit)
那么对于显示一个三维模型呢?我们来看看一个基础的完整的图形渲染管线:下图是一个最基础的图形渲染管线,其中绿色的部分是可编程的阶段,包括顶点着色器、片元着色器,红色的部分是固定流水线单元(Fixed-pipeline Unit),为了效率由硬件直接实现。
阶段 1:Input Assembler
Input Assembler 输入装配器,是图形流水线的第一个**固定阶段**(不可编程,但可配置)。它直接与应用程序(CPU端)提交的数据打交道。💡PS:Input Assembler 是 Direct3D 中的明确标识的管线阶段,用于组装从 Vertex Buffers 和 Index Buffers 中的顶点数据,形成图元。
但在 OpenGL 中没有对应的阶段,这一“组装”的工作需要手动处理,通过
glVertexAttribPointer
系列函数来指定顶点属性的格式和布局。
阶段 2: Vertex Shader
顶点着色器(Vertex Shader)是 图形渲染管线中第一个可编程的 部分,图形渲染管线的第一个可编程部分是顶点着色器(Vertex Shader),它把一个单独的顶点 Vertex 作为输入,经过 shader 处理后输出,顶点着色器主要的目的是对 3D 坐标进行 MVP 变换,变换为屏幕坐标,同时顶点着色器允许我们对顶点属性进行一些基本处理。
阶段 3:Primitive Assembler
图元装配(Primitive Assembly)阶段也是一个固定阶段,用于将顶点着色器(或几何着色器)输出的所有顶点作为输入(如果是GL_TRIANGLE,那么就是一个三角形),并将所有的点装配成指定图元的形状。
阶段 4:Rasterizer
图元装配阶段的输出会被传入光栅化阶段(Rasterization Stage),光栅化其实就是找出三角形所覆盖区域对应的屏幕上的像素,从而将这些像素提供给片元着色器。光栅化阶段也是一个固定流水线单元,是一个算法固定的操作,由硬件直接处理,不可编程。
在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出屏幕范围以外的所有像素,用来提升执行效率。
💡 注意光栅化本质上是将顶点信息插值到图源覆盖的每个像素上。
这也可以解释从顶点着色器传值给片元着色器的值,会经过插值。这其实就是光栅化阶段后,片元着色器接收到的已经不是某个顶点的原始输出数据了,而是经过光栅化器插值后的、针对当前这个特定片元的值,这些变量在片元着色器中声明为
in
变量。
阶段 5: Pixel Shader
片段着色器 Pixel Shader 的主要目的是计算一个像素的最终颜色,这也是所有高级效果产生的地方。跟顶点着色器 Vertex Shader 一样,Pixel Shader 也是一个单入单出的结构,#version 330 core
out vec4 FragColor;
in vec4 vertexColor; // 从顶点着色器传来的输入变量(名称相同、类型相同)
void main()
{
FragColor = vertexColor;
}
通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。
阶段 6:Output Merger
在所有对应颜色值确定以后,最终的对象将会被传到最后一个阶段,我们叫做**Output Merger**(或者Alpha测试和混合)阶段,这个阶段检测片段的对应的深度(和模板(Stencil))值,用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。这个阶段也会检查alpha值并对物体进行混合(Blend)。所以,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同。
Output Merger 也是固定流水线单元;
总结
+ 整个图形流水线要经过几个阶段,其中 Vertex Shader 和 Pixel Shader 是我们可以进行编程的阶段。 + 我们也必须定义至少一个顶点着色器和一个片段着色器(因为GPU中没有默认的顶点/片段着色器)。 + Vertex Shader 和 Pixel Shader 编写就像是在编写一个回调函数,他们会对每一个顶点、每一个像素上执行。二、逻辑上的模块划分
上一部分我们介绍了最基本的图形流水线,随着时代的发展,新的需求逐渐出现,现代的 GPU 不仅仅用于图形渲染,还可以进行通用计算、用于神经网络训练的 TensorCore 、用于视频编解码等多个领域,这些不同的领域在 GPU 上都是独立的模块,具有独立的流水线。我们来看看现在的 GPU 流水线是什么样的,以及他是如何发展来的。1、 图形
Geometry Shader
Vertex Shader 和 Pixel Shader 都是单入单出的结构,只能处理单个顶点和像素。但如果我们要处理的是一个图元(三角形),就没法处理了。这个需求催生了几何着色器 Geometry Shader 的出现, Geometry Shader 是渲染管线中一个可选的可编程阶段,它位于顶点着色器之后、图元装配和光栅化之前。几何着色器的输入是一个完整的图元及其所有顶点数据(例如,一个三角形需要输入3个顶点),而它的输出是零个、一个或多个全新的图元。
换句话说,它是单入多出的结构。
注:Geometry Shader 看起来非常灵活,但实际使用时往往会发现性能很差,这是因为如由于灵活,硬件无法做各种假设来提升性能,只能实现的非常保守。
tessellation
tessellation 的出现是因为对三角形细分需求的逐步增加,由于使用 Geometry shader 性能较差,GPU 流水线在 Vertex shader 后增加了专门的tessellation 功能,他是由三个部分组成 Hull Shader、tessellation、Domain Shader。
2、 计算
GPGPU 通用 GPU
由于 GPU 在图形渲染领域强大的计算能力,逐渐出现了使用 GPU 进行其他领域更加通用的并行计算的想法。最早的想法是渲染一个覆盖屏幕的大三角形,在 Pixel Shader 里做通用的并行计算,相当于每一个像素 Pixel 是一个线程, 这种方式虽然很“hack”,但也产出了很多的成果,这个方向就是** GPU 通用计算(GPGPU)**。
这种方式虽然解决了一些问题,但也存在学习成本高(开发人员需要学习整个图形流水线)、存在性能浪费(还是需要通过整个图形流水线包括顶点着色器)。
这个需求进一步催生了有硬件支持的 GPGPU,不需要再通过图形流水线中那些固定的阶段,同时支持“多入多出”,独立与图形流水线单独存在,应用 GPU 上的计算单元进行通用计算的 shader 叫做 compute shader
。
3、 光线追踪
随着游戏对画面真实感的要求越来越高,基础图形流水线采用光栅化渲染方式,对于实现高质量画面往往要采用很多的 Hack,光线跟踪这一古老的技术逐渐引起了人们注意。由于光线跟踪与光栅化渲染方式有着完全不一样的流程,长期以来研究人员一直在研究如何利用先有的 GPU 硬件实现光线跟踪,这样的需求随着 GPU 在硬件层面提供光线跟踪支持得到了实质的发展。
三、部署到硬件
前边介绍了 GPU 在逻辑模块上的划分,本节我们来看下具体对应到硬件上,GPU 是如何设计的。unified shader
最开始的 GPU 设计上,Vertex Shader 和 Pixel Shader 有对应的处理单元进行处理,比如 2003 年的 GeForce FX 5600,有 2 个 Vertex Shader 单元和 4 个 Pixel Shader 单元,这意味着当顶点和像素的工作量是 1:2 的时候,他们才能发挥出最高效率。对于如果有一堆很小的三角形挤到一起(顶点多像素少)或很大的三角形覆盖(顶点少像素多)的情形都不能发挥出很高的效率。最初这样设计是因为人们通常只用 Vertex Shader 处理坐标数据,需要有较高的精度处理能力;而 Pixel Shader 只处理纹理,只需低精度运算器,但需要采样器。
而随着需求的发展,Vertex Shader 和 Pixel Shader 的能力界限逐渐变的模糊。大规模地形渲染的需求,使得 Vertex Shader 得能读取纹理,而 Pixel Shader 进行通用计算的需求,也使得 Pixel Shader 得能处理高精度数据,最终就是两种处理单元统一了起来,叫做 <font style="color:rgb(15, 17, 21);">unified shader</font>
,这样 GPU 也因为一致性而变得简单。
在 GPU 工作时,由调度器根据工作任务进行动态分配,决定哪些<font style="color:rgb(15, 17, 21);"> unified shader</font>
用于处理 Pixel Shader,哪些处理 Vertex Shader。
最终的结果就是虽然图形流水线中有那么多的 shader,但在硬件层面他们的执行单元都是一样的。
四、完整的软件栈
理想的软件分层体系
理想的关于 GPU 软件分层体系:-
API
应用程序接口层:为应用程序提供统一的编程接口(如 OpenGL、Vulkan、DirectX、CUDA 等),开发者使用 API 编写图形或计算任务,无需直接处理底层硬件细节; -
OS
操作系统层; -
DDI
设备驱动程序接口:是操作系统与 GPU 驱动程序之间的标准接口。由操作系统定义和实现; -
Driver
驱动程序:将 API 调用翻译成 GPU 能理解的指令,管理 GPU 资源(如显存、命令队列),由 GPU 硬件厂商(如NVIDIA、AMD)实现,并且必须严格遵循DDI的规范; - GPU:GPU 接收由驱动程序提交的命令和数据,进行并行处理
但现实情况下会不一样,操作系统就包括了用户态和内核态。
Direct 3D
第一个例子是微软的 Direct 3D(D3D),这个 API 不跨平台(windows),但跨厂商(如NVIDIA、AMD)。它将驱动拆分为了(用户态UMD + 内核态KMD),并引入引入核心调度器(DXGK),将厂商实现驱动程序由作文题变为填空题,减少了驱动程序开发工作量。
Direct 3D 采用自顶向下的模式,由微软定义 API,厂商来进行实现,不方便进行扩展。在 GPU 拥有新功能的硬件支持后,只有等待 Direct 3D 发布了新版本才能支持。
另外 D3D 的 每个版本的 API 是不兼容的,这意味着每出一版 D3D,程序都得大改才能用上。
OpenGL
OpenGL 是跨平台且跨厂商的,由 Khronos (开源组织)发布。在不同的操作系统上,OpenGL
在 Windows 上,Windows 只提供了一个框架可安装用户驱动 ICD,让硬件厂商来实现 OpenGL runtime 的 UMD。
在 Linux 上,有两种方式,一种是完全由厂商来实现 UMD 和 KMD;另一种是基于 Mesa 框架。
OpenGL 的 API 设计是向下兼容的,之前的代码往往新版本也能用。
完
原教程视频中还有关于图形流水线中不可编程单元(重点光栅化)、光线跟踪流水线等部分内容,讲的也非常好。因为我本职工作涉及 WebGL 内容,所以对于暂时没有对这块内容不太有耐心写下来。大家由想了解的推荐看原视频。从日本新首相候选人,看中日的未来

日本石破茂辞职后,等下一任首相上台,整不好我们的61和5C就有机会出来活动筋骨了。因为日本正在一边坠入倒退的深渊,一边变得更加的极端。石破茂的辞职、美国反应的冷淡,揭掉了日本最后一块遮羞布,使日本千疮百孔的混乱政坛和连环暴雷的经济困局赤裸裸地暴露在世人面前,日本正在发生什么?
下载虎嗅APP,第一时间获取深度独到的商业科技资讯,连接更多创新人群与线下活动
React Native DApp 开发全栈实战·从 0 到 1 系列(兑换-合约部分)
前言
本文借助 Solidity 0.8、OpenZeppelin 与 Chainlink 喂价,构建一套 链上即时汇率结算、链下可信价格驱动 的微型兑换系统。本文将带你完成:
- 部署可铸造 ERC-20(BoykayuriToken,BTK)
- 部署 Chainlink 风格喂价合约(MockV3Aggregator),本地即可模拟 ETH/USD = 2000 的实时价格
- 部署 SwapToken —— 接收 ETH、按市价折算 USD、并立即向用户发放等值 BTK
- 使用 Hardhat 本地网络 +
hardhat-deploy
插件一键启动,5 条指令完成编译、测试、部署全流程 无需前端,无需真实 LINK,即可体验 "价格输入 → 汇率计算 → 代币闪兑" 的完整闭环,为后续接入主网喂价、多币种池子、流动性挖矿奠定可复用的脚手架。
智能合约
代币合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract SwapToken is Ownable {
AggregatorV3Interface internal priceFeed;
IERC20 public token;
uint public constant TOKENS_PER_USD = 1000; // 1 USD = 1000 MTK
constructor(address _priceFeed, address _token) Ownable(msg.sender) {
priceFeed = AggregatorV3Interface(_priceFeed);
token = IERC20(_token);
}
function swap() public payable {
uint usd = getEthInUsd(msg.value);
uint amount = usd * TOKENS_PER_USD;
require(token.balanceOf(address(this)) >= amount, "Not enough liquidity");
token.transfer(msg.sender, amount);
}
function getEthInUsd(uint ethAmount) public view returns (uint) {
(, int price, , , ) = priceFeed.latestRoundData(); // price in 8 decimals
uint ethUsd = (ethAmount * uint(price)) / 1e18; // ETH amount in USD (8 decimals)
return ethUsd / 1e8; // return USD amount
}
receive() external payable {}
}
喂价合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
contract MockV3Aggregator is AggregatorV3Interface {
uint256 public constant versionvar = 4;
uint8 public decimalsvar;
int256 public latestAnswer;
uint256 public latestTimestamp;
uint256 public latestRound;
mapping(uint256 => int256) public getAnswer;
mapping(uint256 => uint256) public getTimestamp;
mapping(uint256 => uint256) private getStartedAt;
string private descriptionvar;
constructor(
uint8 _decimals,
string memory _description,
int256 _initialAnswer
) {
decimalsvar = _decimals;
descriptionvar = _description;
updateAnswer(_initialAnswer);
}
function updateAnswer(int256 _answer) public {
latestAnswer = _answer;
latestTimestamp = block.timestamp;
latestRound++;
getAnswer[latestRound] = _answer;
getTimestamp[latestRound] = block.timestamp;
getStartedAt[latestRound] = block.timestamp;
}
function getRoundData(uint80 _roundId)
external
view
override
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
return (
_roundId,
getAnswer[_roundId],
getStartedAt[_roundId],
getTimestamp[_roundId],
_roundId
);
}
function latestRoundData()
external
view
override
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
return (
uint80(latestRound),
latestAnswer,
getStartedAt[latestRound],
latestTimestamp,
uint80(latestRound)
);
}
function decimals() external view override returns (uint8) {
return decimalsvar;
}
function description() external view override returns (string memory) {
return descriptionvar;
}
function version() external pure override returns (uint256) {
return versionvar;
}
}
兑换合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract SwapToken is Ownable {
AggregatorV3Interface internal priceFeed;
IERC20 public token;
uint public constant TOKENS_PER_USD = 1000; // 1 USD = 1000 MTK
constructor(address _priceFeed, address _token) Ownable(msg.sender) {
priceFeed = AggregatorV3Interface(_priceFeed);
token = IERC20(_token);
}
function swap() public payable {
uint usd = getEthInUsd(msg.value);
uint amount = usd * TOKENS_PER_USD;
require(token.balanceOf(address(this)) >= amount, "Not enough liquidity");
token.transfer(msg.sender, amount);
}
function getEthInUsd(uint ethAmount) public view returns (uint) {
(, int price, , , ) = priceFeed.latestRoundData(); // price in 8 decimals
uint ethUsd = (ethAmount * uint(price)) / 1e18; // ETH amount in USD (8 decimals)
return ethUsd / 1e8; // return USD amount
}
receive() external payable {}
}
编译指令:npx hardhat compile
测试合约
const { ethers } = require("hardhat");
const { expect } = require("chai");
describe("SwapToken", function () {
let SwapToken, MockToken, MockV3Aggregator;
let owner, user;
beforeEach(async () => {
[owner, user] = await ethers.getSigners();
await deployments.fixture(["MockV3Aggregator","token","SwapToken"]);
const MockTokenAddress = await deployments.get("MyToken"); // 存入资产 // 奖励代币(USDC)
const MockV3AggregatorAddress = await deployments.get("MockV3Aggregator");
const SwapTokenAddress = await deployments.get("SwapToken");
MockToken = await ethers.getContractAt("MyToken", MockTokenAddress.address);
MockV3Aggregator = await ethers.getContractAt("MockV3Aggregator", MockV3AggregatorAddress.address);
SwapToken = await ethers.getContractAt("SwapToken", SwapTokenAddress.address);
// 给SwapToken合约铸造资产
await MockToken.mint(await SwapToken.getAddress(), ethers.parseEther("1000000"));
console.log('name',await MockToken.name())
console.log("symbol",await MockToken.symbol())
console.log(await MockV3Aggregator.latestAnswer())
});
it("Should swap ETH for MTK", async function () {
const ethAmount = ethers.parseEther("1"); // 1 ETH = 2000 USD = 2,000,000 MTK
await SwapToken.connect(user).swap({ value: ethAmount });
const balance = await MockToken.balanceOf(user.address);
console.log(balance)
expect(balance).to.equal(2000 * 1000); // 2,000,000 MTK
});
});
测试指令:npx hardhat test ./test/xxxx.js
部署合约
module.exports = async ({getNamedAccounts,deployments})=>{
const getNamedAccount = (await getNamedAccounts()).firstAccount;
const secondAccount= (await getNamedAccounts()).secondAccount;
console.log('secondAccount',secondAccount)
const {deploy,log} = deployments;
const MyAsset = await deployments.get("MyToken");
//执行MockV3Aggregator部署合约
const MockV3Aggregator=await deploy("MockV3Aggregator",{
from:getNamedAccount,
args: [8,"ETH/USDC", 200000000000],//参数
log: true,
})
console.log("MockV3Aggregator合约地址:", MockV3Aggregator.address);
const SwapToken=await deploy("SwapToken",{
from:getNamedAccount,
args: [MockV3Aggregator.address,MyAsset.address],//参数 喂价,资产地址
log: true,
})
// await hre.run("verify:verify", {
// address: TokenC.address,
// constructorArguments: [TokenName, TokenSymbol],
// });
console.log('SwapToken 兑换合约地址',SwapToken.address)
}
module.exports.tags = ["all", "SwapToken"];
部署指令:npx hardhat deploy --tags token,MockV3Aggregator,SwapToken
总结
通过本文,我们完成了:
- 价格层:MockV3Aggregator 遵循 Chainlink 接口,可无缝替换为主网喂价
-
资产层:ERC-20 代币自带
mint
与Ownable
,方便快速补充流动性 -
兑换层:SwapToken 利用
latestRoundData()
实时计算 ETH→USD→BTK 数量,全程链上可查 - 脚本层:Hardhat 脚本化部署 + 测试固件,保证"一键重置、秒级回滚",让迭代安全又高效
后续优化:
- 把 Mock 喂价替换为 ETH/USD 主网聚合器
- 引入 Uniswap V2 风格的流动性池,实现双向兑换
🚀🚀🚀 RichLab - 花呗前端团队招贤纳士 - 【转岗/内推/社招】
欢迎加入我们!与AI共舞,定义下一代智能金融体验
直接联系 👉🏻👉🏻👉🏻 邮箱:alei.xl@antgroup.com
💫 我们是谁?
RichLab - 花呗前端团队,一个充满活力与创新的技术团队,我们正在做一件超酷的事情 —— 让AI与金融完美融合,重新定义用户体验!
我们不是传统的前端团队,我们是金融科技前沿的探索者,是AI时代的先行者。在这里,每一行代码都可能改变千万用户的金融生活,每一次创新都可能引领行业潮流。
🎯 我们在做什么?
🌟 核心使命
负责亿级用户规模的AI产品核心功能研发,直接参与定义下一代智能金融交互。
简单来说,我们就是那个让花呗变得更智能、更懂你的团队!
🔥 AI创新业务探索方向
花呗正从“消费金融工具”进化为“懂你的成长型生活伙伴”。
- 智能助理是服务大脑,统一调度各类助手,以亲密度为指挥棒,实现场景化、个性化的服务分发;
- 多模态内容生成是情感触点,借力AIGC技术将用户的日常消费账单转化为具有情感温度的视觉化内容,让每一笔消费都有温度和记忆;
- 端智能服务是感知神经,通过端侧实时感知用户行为、环境与意图,驱动智能助理提前介入,实现“比你更懂你”的主动式服务体验。
三者深度融合,共同构建一个有温度、有记忆、会思考的AI金融智能服务体。
🎪 为什么选择我们?
1️⃣ 技术作品丰富 💪
- 拥有金融级稳定性的前端工程体系
- 孵化了Rococo、Bakery、Lever、Tina、Galacean等备受赞誉的技术产品
2️⃣ AI前沿探索 🤖
- 在AI领域重点投入,积极探索储备
- 拥抱AI研发范式的全面转型
- 打造有创意、有特色、行业领先的金融产品体验
3️⃣ 技术氛围浓厚 🌟
- 多次在D2、SEEConf、Qcon、CCF中国计算机学会、W3C大会、CGS中国图学学会、中关村论坛等顶级会议担任嘉宾
- 开放、协同、分享的浓厚技术氛围
- 鼓励参与开源社区建设
4️⃣ 团队文化超赞 ❤️
- 倡导「专业、匠心、自由、有爱」的技术文化
- 重视团队成员成长,把个人发展作为第一优先级
- 成员之间开放、平等、简单,充满极强的信任感
5️⃣ 生活丰富多彩 🎸
- RichBand和吉他班:部门有自己的乐队,且为 0 基础同学提供系统性器乐培训,全面支持个人爱好
- 健身文化:找到你的健身搭子、骑行搭子、跑步搭子
- 日常惊喜:说走就走的团建、奶茶、黑珍珠,老板们常常制造惊喜
🎯 岗位要求
✅ 基础要求
- 有扎实的计算机基础知识,熟悉常用的数据结构、算法和设计模式,在日常研发中灵活使用。
- 掌握 HTML/CSS/JavaScript,熟悉 React/Vue/Angular 等前端框架。
- 有丰富的前端性能优化及移动端适配经验。
- 了解金融/互联网信贷业务,有相关行业经验者优先。
- 具备良好的服务意识、团队协作精神,学习能力强。
💡 加分项
- 有相关AI产品业务经验
- 有开源项目经验
- 长期维护有影响力的技术博客
🚀 在这里你能获得什么?
✨ 技术成长
- 接触最前沿的AI技术
- 参与亿级用户规模的产品开发
- 在金融科技领域深耕,成为行业专家
✨ 个人发展
- 包容、开放的心态拥抱人才多样性
- 不论你是业务型、技术型、领域型、架构型、综合型人才,都能找到最适合的位置
- 团队成员成长和发展是管理第一优先级
✨ 生活体验
- 专业的技术氛围 + 轻松的生活氛围
- 丰富的团队活动和文化建设
- 与志同道合的伙伴一起成长
🎪 团队风采
🎉 团建、奶茶、黑珍珠,老板们的惊喜,让工作充满乐趣~
🎸 艺术细胞。我们不仅会写代码,还会玩音乐!部门有自己的乐队,定期排练演出,让技术人的艺术细胞得到充分释放。
💪 健身文化。技术要强,身体也要棒!在这里,你可以轻松找到健身搭子、骑行搭子、跑步搭子,大家一起在健身房共同进步。
🔥 更多资讯见「视频号:RichLab后花园」
📮 如何加入我们?
联系方式
🌟 写在最后
如果你:
- 对AI技术充满热情
- 想要在金融科技前沿探索
- 希望在一个专业、有爱、自由的团队中成长
- 想要参与定义下一代智能金融体验
那么,RichLab - 花呗前端团队就是你的不二之选! 我们相信,技术改变世界,AI改变金融。加入我们,让我们一起用代码和AI,创造更美好的金融未来!
🎯 还在等什么?快来加入我们,一起定义未来! RichLab - 花呗前端团队,期待与你相遇!
Oxc 和 Rolldown Q4 更新计划速览!🚀🚀🚀
前言
今天 Oxc 和 Rolldown 先后发布了 Q4 季度的更新计划,一起来看看吧!
往期精彩推荐
- 字节也在用的 @tanstack/react-query 到底有多好用!🔥🔥🔥
- 🚀🚀🚀 告别复制粘贴,这个高效的 Vite 插件让我摸鱼🐟时间更充足了!
- 🔥🔥🔥 原来在字节写代码就是这么朴实无华!🔥🔥🔥
- 更多精彩文章欢迎关注我的公众号:萌萌哒草头将军
正文
Oxc
Q4 的重点在于推出 Alpha 和 Beta 版本的功能,扩展 linter、格式化和压缩能力:
- Oxlint Custom JavaScript Plugin Alpha
Oxlint将推出自定义JavaScript插件的Alpha版,支持开发者创建专属规则,集成到570+现有规则中。Oxlint已比ESLint快50-100倍,此功能将增强灵活性,适配特定项目需求,如自定义代码规范或复杂逻辑检查。Alpha版提供API和示例,开发者可通过GitHub测试。
- Formatter Alpha
格式化工具Alpha版将支持JavaScript和TypeScript代码的自动格式化,类似Prettier但速度更快。功能包括统一的缩进、引号处理和分号规则,优化与VS Code等编辑器的集成。Alpha阶段将验证核心算法,适合大规模代码库。
- Minifier Beta
压缩工具进入Beta阶段,利用Rust并行处理能力,优于Terser,生成更小的输出文件。支持死代码消除、变量重命名等高级选项,适用于生产环境。Beta版将进一步稳定性能。
- Type-Aware Linting Alpha
类型感知linter的Alpha版结合TypeScript类型信息,提供更精准的代码检查,如类型不匹配或空指针检测。相比传统linter,它能减少运行时错误,适合混合JS/TS项目。
Rolldown
Rolldown作为Rollup API兼容的捆绑器,目标是取代Vite中的esbuild,提供10-30倍性能提升。Q4计划聚焦于全功能捆绑、优化和Vite生态集成:
-
实现完整的捆绑流程,从入口到依赖,简化Vite生产构建,取代多工具组合,提供高效优化。
-
懒编译功能按需加载模块,缩短初始加载时间,结合Vite热重载,显著提升开发体验。
-
将importmaps集成到Vite,支持浏览器原生模块映射,简化第三方库导入,增强兼容性。
-
提高捆绑包大小:
- Lazy Barrel Optimization:优化barrel文件懒加载,减少不必要导入。
- More Cross-Chunk Optimizations:跨代码块优化,如共享模块提取和树摇,缩小bundle体积10-20%。
- TS Const Enum Optimization:内联TypeScript常量枚举,减少运行时开销。
-
稳定Vite插件支持,确保无缝兼容,修复边缘问题并提供迁移指南。
-
自动解析tsconfig.json,简化TypeScript项目配置。
-
升级文档站点,提供详细指南、API参考和示例,方便开发者集成。
-
支持模块联邦,实现微前端动态加载,性能优于Webpack的类似功能。
最后
如果你期待重要的功能不再上述更新列表中,可以去社区积极反馈哈~
今天的分享就这些了,感谢大家的阅读,如果文章中存在错误的地方欢迎指正!
往期精彩推荐
- 字节也在用的 @tanstack/react-query 到底有多好用!🔥🔥🔥
- 🚀🚀🚀 告别复制粘贴,这个高效的 Vite 插件让我摸鱼🐟时间更充足了!
- 🔥🔥🔥 原来在字节写代码就是这么朴实无华!🔥🔥🔥
- 更多精彩文章欢迎关注我的公众号:萌萌哒草头将军