ERC-6909 最小多代币标准
ERC-6909 Token标准是 ERC-1155 Token标准的一种简化替代方案。
ERC-1155 标准引入了一种多Token接口,使得单个智能合约能够结合可替代的和不可替代的Token(即,ERC20 和 ERC721)。
ERC-1155 解决了多个挑战,例如降低部署成本、最小化以太坊区块链上的冗余字节码,以及简化多Token交易的Token批准流程。
然而,由于每次转账都强制要求回调、强制包含批量转账,以及缺乏对单操作员批准方案的细粒度控制,它引入了一些膨胀和气体低效问题。ERC-6909 通过消除合约级回调和批量转账,并用混合(限额-操作员)权限方案替代单操作员的凭证方案,从而解决了这些缺点,以实现颗粒化的Token管理。
注意: 以下部分假设读者对 ERC-1155 标准及其概念有一定的了解。如果你不熟悉,请在继续之前阅读相关内容。
ERC-6909 和 ERC-1155 标准的对比
ERC-6909 移除转账的回调要求
ERC-1155 规范要求 safeTransferFrom
和 safeBatchTransferFrom
检查接收账户是否为合约。如果是,则必须在接收合约账户上调用 ERC1155TokenReceiver 接口函数(onERC1155Received
,onERC1155BatchReceived
)以检查其是否接受转账。
这些回调在某些情况下是有用的。然而,对于希望不使用这种行为的接收方,它们是不必要的外部调用。回调影响接收合约账户的Gas成本和代码大小,因为它们需要实现多个回调(即,通过 onERC1155Received
,onERC1155BatchReceived
)并返回魔术的 4 字节值以接收Token。相比之下,ERC-6909 的实现者可以自定义他们的回调架构。
ERC-6909 省略了批量转账逻辑
尽管批量转账有时是有益的,但在 ERC-6909 标准中故意省略,以允许开发者根据特定执行环境实施批量转账逻辑。开发者可以根据自己的需要实现批量转账,而无需仅仅为了遵循标准而添加额外的批量转账函数。
下面展示的 safeBatchTransferFrom
函数在 ERC-1155 标准中执行批量转账。然而,其强制的包含为不需要它们的应用程序增加了膨胀:
// ERC-1155
function safeBatchTransferFrom(address _from,address _to,uint256[] calldata _ids,uint256[] calldata _values,bytes calldata _data
) external;
以下是 ERC-6909 transferFrom 函数。我们可以看到批量特性和 _data
参数已被删除。
// ERC-6909
function transferFrom(address sender,address receiver,uint256 id,uint256 amount
) public returns (bool) {if (sender != msg.sender && !isOperator[sender][msg.sender]) {uint256 senderAllowance = allowance[sender][msg.sender][id];if (senderAllowance < amount) revert InsufficientPermission();if (senderAllowance != type(uint256).max) {allowance[sender][msg.sender][id] = senderAllowance - amount;}}if (balanceOf[sender][id] < amount) revert InsufficientBalance();balanceOf[sender][id] -= amount;balanceOf[receiver][id] += amount;emit Transfer(msg.sender, sender, receiver, id, amount);return true;
}
ERC-6909 同时支持全局批准和细粒度限额
// 在 ERC-1155 →
function setApprovalForAll(address _operator,bool _approved
) external;
上面展示的 setApprovalForAll
函数是 ERC-1155 中的全局操作员模型,允许一个账户授权另一个账户管理(作为操作员)其所有Token ID 的操作。一旦被授权,操作员可以随意转移授权账户拥有的任何数量的任何Token ID。
虽然这种方法简化了委托,但缺乏细粒度控制:
- 没有办法授予特定于单个Token ID 或数量的权限。
- 这种全有或全无的方法不适合需要受控权限的场景。
为引入细粒度控制,ERC-6909 混合操作员权限方案包含以下内容:
- 来自 ERC-1155 的操作员模型,
- 和受 ERC-20 启发的限额模型。
ERC-6909 中的操作员模型
在下面的 ERC-6909 setOperator
函数中,spender
变量被设置为操作员,并被授权无条件的权限,以转移账户所拥有的所有Token ID 而没有限额限制。
function setOperator(address spender, bool approved) public returns (bool) {isOperator[msg.sender][spender] = approved;emit OperatorSet(msg.sender, spender, approved);return true;
}
ERC-6909 中的限额模型
限额模型引入了一种特定于Token和数量的控制系统,其中一个账户可以为特定Token ID 设置有限的限额。
例如,Alice 可以允许 Bob 转移 100 个 ID 为 42 的Token,而不授予对其他Token ID 或不受限制的数量的访问权限,使用下一个展示的 ERC-6909 中的 approve
函数。
function approve(address spender, uint256 id, uint256 amount) public returns (bool) {allowance[msg.sender][spender][id] = amount;emit Approval(msg.sender, spender, id, amount);return true;
}
在 approve
中的 spender
变量是被授权代表Token所有者转移特定金额的特定Token ID 的账户。
例如,Token所有者可以允许 spender
转移 <= 100 个特定Token ID。或者,他们还可以通过将限额设置为 type(uint256).max
来为特定Token ID 授予无限制的批准。
ERC-6909 并没有指定是否应扣减设置为 type(uint256).max
的限额。相反,这种行为留给实现者的自由裁量权,类似于 ERC-20。
核心数据结构
ERC-6909 实现使用三个映射来更新账户余额和批准状态。
balanceOf:ID 的所有者余额
balanceOf
映射跟踪特定Token ID 由地址 (owner
) 持有的余额。映射中的 owner => (id => amount)
结构表示单个所有者可以持有多个Token,并通过各自的 ID 跟踪其余额。
mapping(address owner => mapping(uint256 id => uint256 amount)) public balanceOf;
allowance:ID 的支出者限额
允许映射定义支出者在所有者的授权下可以转移多少特定Token(ID)。它促进了对Token支出的细粒度控制。
mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) public allowance;
例如,allowance[0xDEF...][0x123...][5]
将返回所有者 0xDEF...
允许支出者 0x123...
转移的Token(ID 为 5)的数量。
isOperator:操作员批准状态
mapping(address owner => mapping(address operator => bool isOperator)) public isOperator;
该映射跟踪支出者是否被批准作为拥有地址的所有Token的操作员。例如,isOperator[0x123...][0xABC...]
返回 true
如果地址 0xABC...
被允许支出地址 0x123...
所拥有的Token;否则返回 false
。
核心 ERC-6909 功能及其数据参数
转账函数
该规范并未遵循 ERC-721 和 ERC-1155 中的“安全转账机制”,因为由于其向任意合约的外部调用被认为是误导性的。ERC-6909 使用 transfer
和 transferFrom
函数,详情如下。
转账:
ERC-6909 的 transfer
函数表现得与 ERC-20 的 transfer
相同,只不过它适用于特定的Token ID。该函数接受接收地址、Token的 ID 和要转移的金额作为输入参数,并使用 balanceOf
映射更新余额。与 ERC-20 转账函数类似,成功执行事务时返回 true 是必要的。
// ERC-20 接口转账函数function transfer(address _to, uint256 _value) public returns (bool)// ERC-6909 转账函数参考实现// @notice 将数量 id 从调用者转移至接收者。// @param receiver 接收者的地址。// @param id Token的 ID。// @param amount Token的数量。function transfer(address receiver, uint256 id, uint256 amount) public returns (bool) {if (balanceOf[msg.sender][id] < amount) revert InsufficientBalance(msg.sender, id);balanceOf[msg.sender][id] -= amount;balanceOf[receiver][id] += amount;emit Transfer(msg.sender, msg.sender, receiver, id, amount);return true;}
transferFrom:
ERC-6909 的 transferFrom 函数与 ERC-20 的不同之处在于它要求提供一个Token ID。此外,它还检查操作员的批准以及限额。
该函数首先检查 if (sender != msg.sender && !isOperator[sender][msg.sender])
,确保调用者 (msg.sender
) 是:
- 所有者(
sender
),或者 - 已批准的操作员(
isOperator[sender][msg.sender] == true
)。
如果 msg.sender
不是所有者也不是已批准的操作员,函数将检查调用者是否具有 足够的限额 以进行转移。如果有限额但未设置为无限制(type(uint256).max
),则从限额中扣除转移的 amount
。
此外,标准规定如果调用者是操作员或 sender
,则该函数不应扣减调用者对于Token id
的 allowance
中的 amount
。
// ERC-6909 transferFromfunction transferFrom(address sender, address receiver, uint256 id, uint256 amount) public returns (bool) {if (sender != msg.sender && !isOperator[sender][msg.sender]) {uint256 senderAllowance = allowance[sender][msg.sender][id];if (senderAllowance < amount) revert InsufficientPermission();if (senderAllowance != type(uint256).max) {allowance[sender][msg.sender][id] = senderAllowance - amount;}}if (balanceOf[sender][id] < amount) revert InsufficientBalance();balanceOf[sender][id] -= amount;balanceOf[receiver][id] += amount;emit Transfer(msg.sender, sender, receiver, id, amount);return true;
}
approve:
approve
函数允许调用者(msg.sender
)向支出者授予特定Token(ID)的特定限额。这会更新限额映射以反映新的限额并发出 Approval
事件。
function approve(address spender, uint256 id, uint256 amount) public returns (bool) {allowance[msg.sender][spender][id] = amount;emit Approval(msg.sender, spender, id, amount);return true;
}
setOperator:
setOperator
函数允许调用者(msg.sender
)通过将批准参数设置为 true 或 false 来授予或撤销特定地址(spender
)的操作员权限。该函数会相应地更新 isOperator
映射,并发出 OperatorSet
事件,以通知外部监听器有关更改的情况。
function setOperator(address spender, bool approved) public returns (bool) {isOperator[msg.sender][spender] = approved;emit OperatorSet(msg.sender, spender, approved);return true;
}
ERC-6909 中的事件与日志
ERC-6909 定义了关键事件,以跟踪多Token合约中的Token转移、批准及操作员权限。
1. 转移事件:
/// @notice 转移发生时发出的事件。event Transfer(address caller, address indexed sender, address indexed receiver, uint256 indexed id, uint256 amount);
ERC-6909 中的 Transfer
事件用于跟踪Token的移动,必须在以下条件下发出:
- 转移Token
id
的amount
从一个账户到另一个账户时,将记录sender
、receiver
、token ID
和转移的amount
。 - 当创建新Token时,事件必须以
sender
为零地址(0x0
)发出。 - 当Token被销毁时,事件必须以接收方为零地址(
0x0
)发出,以表示Token被移除。
2. OperatorSet 事件:
/// @notice 操作员被设置时发出的事件。
event OperatorSet(address indexed owner, address indexed spender, bool approved);
OperatorSet
事件每当所有者分配或撤销另一个地址的操作员权限时都会发出。事件记录所有者的地址、支出者的地址以及更新的批准状态(true
表示授予,false
表示撤销)。
3. Approval 事件:
/// @notice 批准发生时发出的事件。
event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount);
当所有者设置或更新支出者转移特定金额的特定Token ID 的批准时,必须发出 Approval
事件。事件记录 owner
、spender
、Token id
和批准的 amount
。
现在我们已经探讨了 ERC-6909 与 ERC-1155 之间的差异,以及 ERC-6909 中的核心方法和事件,让我们看看标准的一些实际应用。
Uniswap v4 PoolManager 怎样实现 ERC-6909。
在 Uniswap v3 中,工厂/池模型通过使用 UniswapV3Factory 合约为每个池部署一个单独的合约来创建新的Token对。这种方法增加了Gas成本,因为每个新池都需要新的合约部署。
与此相比,Uniswap v4 引入了一个单例合约(PoolManager.sol),该合约将所有流动性池管理作为其内部状态的一部分,而不是要求单独的合约部署。这一设计显著降低了池创建的Gas成本。
此外,以前版本中涉及 多个 Uniswap 池 的交易需要跨多个合约的Token转移和冗余状态更新。在 Uniswap v4 中,PoolManager 合约可以集中持有用户的 ERC-6909 表示的 ERC-20 Token,而不需要在池中往返转移 ERC-20 Token。
例如,如果用户为Token A 提供流动性,他们后来可以选择提取其股份并接收Token A 作为转移到其钱包的 ERC-20。然而,如果他们选择不提取Token,那么 Uniswap v4 的 PoolManager
可以 铸造其Token余额的 ERC-6909 表示,而无需从合约转移 ERC-20 Token——节省了跨合约调用。这些 ERC-6909 余额允许用户在协议中交易或互动,而无需在钱包之间移动Token。
这意味着当用户后来将Token A 兑换为Token B 时,Uniswap 只是更新他们在池中的 ERC-6909 余额。
注意:ERC-6909 在 Uniswap v4 中不作为 LP 代币使用。
ERC-6909 元数据在单例 DeFi 架构和 NFT 系列中的考虑
以下是 IERC6909Metadata
接口,它定义了如何将 ERC-6909 标准和单独Token相关联的元数据,例如名称、符号和小数,其函数可以根据 id 的不同发展,允许 ERC-6909 中不同的Token具有不同的名称、符号和小数。
/// @notice 包含单个Token元数据的合同。
interface IERC6909Metadata is IERC6909 {/// @notice 给定Token的名称。/// @param id Token的 id。/// @return name Token的名字。function name(uint256 id) external view returns (string memory);/// @notice 给定Token的 символ。/// @param id Token的 id。/// @return symbol Token的符号。function symbol(uint256 id) external view returns (string memory);/// @notice 给定Token的小数位数。/// @param id Token的 id。/// @return decimals Token的小数位数。function decimals(uint256 id) external view returns (uint8);
}
对于 DeFi 协议,我们可能有多个 LP 代币,并且我们可能希望将其标准化为都具有相同的小数,例如 18。然而,我们可能希望名称和符号能够反映池中持有的不同资产。
相比之下,对于 NFT,decimals
值应始终设置为 1,因为 NFT 是不可分割的。
在典型的 NFT 系列(例如 ERC-721)中,所有Token共享相同的名字和符号,以表示整个系列(例如 "CryptoPunks" 和符号 "PUNK")。ERC-6909 使我们能够遵循 ERC-712 规范,其中所有 NFT 在同一系列中共享相同的元数据。
ERC-6909 非同质化Token的实现示例。
ERC-6909 规范并没有明确规定支持非同质化Token的独特方法。但是,可以使用 ERC-1155 规范中描述的 ID 位拆分技术在 ERC-6909 中实现非同质化Token。这种方法使用 位移和加法运算 将集合 ID 和项目 ID 编码在一个 uint256 Token ID 中。
function getTokenId(uint256 collectionId, uint256 itemId) public pure returns (uint256) {return (collectionId << 128) + itemId;
}
下面的 ERC6909MultiCollectionNFT
合约是一个使用 getTokenId
根据 collectionId
和 itemId
生成Token ID 的非同质化Token(NFT)实现示例。
mintNFT
函数确保每个 tokenId
只能被铸造一次,无论地址如何。它使用 mintedTokens
映射跟踪 NFT tokenId
是否已被全球铸造。
由于在 mintNFT
中将 amount
变量设置为 1,因此函数中的 _mint(to, tokenId, amount)
调用将对 tokenId
铸造一份。在任何情况下,如果 amount
> 1,Token将变为可替代,而不是非同质化。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;import "./ERC6909.sol";contract ERC6909MultiCollectionNFT is ERC6909 {struct NFT {string uri;}mapping(uint256 => NFT) private _tokens;mapping(uint256 => string) private _collectionURIs;mapping(uint256 => bool) public mintedTokens;event MintedNFT(address indexed to, uint256 indexed collectionId, uint256 indexed itemId, uint256 tokenId, string uri);// 通过连接 collectionId 和 itemId 计算 Token IDfunction getTokenId(uint256 collectionId, uint256 itemId) public pure returns (uint256) {return (collectionId << 128) + itemId;}function _mint(address to, uint256 tokenId, uint256 amount) internal {balanceOf[to][tokenId] += amount;emit Transfer(msg.sender, address(0), to, tokenId, amount);}function mintNFT(address to, uint256 collectionId, uint256 itemId, string memory uri) external {uint256 amount = 1;uint256 tokenId = getTokenId(collectionId, itemId);require(!mintedTokens[tokenId], "ERC6909MultiCollectionNFT: Token already minted");require(amount == 1, "ERC6909MultiCollectionNFT: Token copies must be 1");_tokens[tokenId] = NFT(uri);mintedTokens[tokenId] = true; // 标记为已铸造_mint(to, tokenId, amount); // amount 被定义为 1。emit MintedNFT(to, collectionId, itemId, tokenId, uri);}function nftBalanceOf(address owner, uint256 tokenId) public view returns (uint256) {return balanceOf[owner][tokenId];}
}
请记住,上面 mintNFT
中的 _mint
调用将余额映射更新为 1,因为这些铸造的Token完全是非同质化的。因此,在此合约中,如果 owner
地址确实铸造了 tokenId
,nftBalanceOf
函数预计总是返回 1。
为了转移Token的所有权,下面的 nftTransfer
函数确保只有 NFT 所有者可以通过验证其余额启动转移,才能允许唯一存在的单位转移。
function nftTransfer(address to, uint256 tokenId) external {require(balanceOf[tokenId][msg.sender] == 1, "ERC6909MultiCollectionNFT: This should be non-fungible.");require(to != address(0), "ERC6909MultiCollectionNFT: transfer to zero address");transfer(to, tokenId, 1);// 在此情况下,数量等于 1。emit Transfer(msg.sender, address(0), to, tokenId, 1);
}
ERC-6909 内容 URI 扩展与元数据 URI JSON 模式
为了标准化 ERC-6909 中的元数据访问,可选的 IERC6909ContentURI
接口为检索合约和Token层次的元数据定义了两个 URI 函数(contractURI
和 tokenURI
)。ERC-6909 标准并不强制Token需要关联 URI 元数据。然而,如果实现中包含这些 URI 函数,返回的 URI 应该指向遵循 ERC-6909 元数据 URI JSON 架构的 JSON 文件。
pragma solidity ^0.8.19;import "./IERC6909.sol";/// @title ERC6909 内容 URI 接口
interface IERC6909ContentURI is IERC6909 {/// @notice 合同级别 URI/// @return uri 合同级别的 URI。function contractURI() external view returns (string memory);/// @notice Token级别 URI/// @param id Token的 ID。/// @return uri Token级别的 URI。function tokenURI(uint256 id) external view returns (string memory);
}
如上所示,ERC-6909 IERC6909ContentURI 接口定义了两个可选的 URI 函数,即 contractURI
和 tokenURI
;每个函数都有其相应的 URI JSON 模式。contractURI
函数(不带参数)返回指向合约级别元数据的单个 URI,而 tokenURI()
返回每个Token ID 特定的 URI。
以下是根据 ERC-6909 标准构造合约 URI JSON 架构的示例。
{"title": "Contract Metadata","type": "object","properties": {"name": {"type": "string","description": "The name of the contract."},"description": {"type": "string","description": "The description of the contract."},"image_url": {"type": "string","format": "uri","description": "The URL of the image representing the contract."},"banner_image_url": {"type": "string","format": "uri","description": "The URL of the banner image of the contract."},"external_link": {"type": "string","format": "uri","description": "The external link of the contract."},"editors": {"type": "array","items": {"type": "string","description": "An Ethereum address representing an authorized editor of the contract."},"description": "An array of Ethereum addresses representing editors (authorized editors) of the contract."},"animation_url": {"type": "string","description": "An animation URL for the contract."}},"required": ["name"]
}
tokenURI
函数则接受一个 uint256
参数 id
,并返回该Token的 URI。如果Token id
不存在,该函数可以回滚。与合约级别的 URI 一样,客户在与合约交互时 必须 替换 URI 中每处 {id}
的出现为实际的Token ID,以访问与该Token相关的正确元数据。
以下是 tokenURI
函数的实现,返回遵循占位符格式的 静态 URI 模板:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;import "./ERC6909.sol";
import "./interfaces/IERC6909ContentURI.sol";contract ERC6909ContentURI is ERC6909, IERC6909ContentURI {/// @notice 合同级 URI。string public contractURI;/// @notice 每个 id 的 URI。/// @return Token的 URI。function tokenURI(uint256) public pure override returns (string memory) {return "<baseuri>/{id}";}
}
以下是根据 ERC-6909 标准构造 URI JSON 架构的示例。
{"title": "Asset Metadata","type": "object","properties": {"name": {"type": "string","description": "Identifies the token"},"description": {"type": "string","description": "Describes the token"},"image": {"type": "string","description": "A URI pointing to an image resource."},"animation_url": {"type": "string","description": "An animation URL for the token."}},"required": ["name", "description", "image"]
}
ERC-6909 规范中的限额和操作员歧义。
考虑一个场景,其中一个账户(A)授予另一个账户(B)操作员权限,并为 B 设置可转移特定金额的Token的限额。
如果 B 代表 A 发起转移,则实现必须确定检查的正确顺序以及限额如何与操作员权限互动。
歧义涉及检查的顺序。合约应该:
- 首先检查限额,如果不足则回滚,即使 B 具有操作员权限。
- 首先检查操作员权限,无论限额如何都允许转移。
在下面的 allowanceFirst
合约中,如果账户 B 具有操作员权限,但限额不足,限额检查将失败,从而导致事务回滚。这可能是反直观的,因为操作员权限通常意味着无限制访问,用户可能会期望事务成功。
相反,在 operatorFirst
合约中,如果实现首先检查操作员权限,将绕过限额检查,事务将基于操作员的无限制访问而成功。
contract operatorFirst {function transferFrom(address sender, address receiver, uint256 id, uint256 amount) public {// 首先检查 `isOperator`if (msg.sender != sender && !isOperator[sender][msg.sender]) {require(allowance[sender][msg.sender][id] >= amount, "insufficient allowance");allowance[sender][msg.sender][id] -= amount;}// -- 剪切 --}
}contract allowanceFirst{function transferFrom(address sender, address receiver, uint256 id, uint256 amount) public {// 首先检查限额是否充足if (msg.sender != sender && allowance[sender][msg.sender][id] < amount) {require(isOperator[sender][msg.sender], "insufficient allowance");}// 错误:当限额不足时,由于算术下溢而发生恐慌,无论调用者是否具有操作员权限。allowance[sender][msg.sender][id] -= amount;// -- 剪切 --}
}
该标准故意将权限检查的决定留给实现者,这给实现者提供灵活性。在一个账户同时拥有操作员权限和不足的限额时,转账行为取决于检查的顺序。
结论
ERC-6909 标准通过移除转账函数中的批量和强制回调,显著提高了 ERC-1155 的效率。去除批量处理允许逐个案例优化,特别是对于汇总或气体敏感的环境。
它还通过混合操作员权限方案引入了可扩展的Token批准控制,更多信息,,https://t.me/+_QibemQqIIg1OTY1。
相关文章:
ERC-6909 最小多代币标准
ERC-6909 Token标准是 ERC-1155 Token标准的一种简化替代方案。 ERC-1155 标准引入了一种多Token接口,使得单个智能合约能够结合可替代的和不可替代的Token(即,ERC20 和 ERC721)。 ERC-1155 解决了多个挑战,例如降…...
07-单链表-单链表基本操作
题目 来源 826. 单链表 - AcWing题库 思路 详见代码,主要思想就是用数组来模拟链表的创建。数组其实跟静态链表等价,由于动态链表动态new对于大数据太过于耗时,因此采用数组的方式。那数组如何起到链表的效果?用下标来索引。 …...
FFMPEG录制远程监控摄像头MP4
手绘效果图 上图是录制功能的HTML前端页面,录制功能和解码视频放在一起。录制功能关键是录制(开始录制按钮)、停止录像按钮。当点击“录制”的时候则会开始录制MP4文件, 当点击停止的时候就会停止录制MP4。经过录制后,则会生成MP4,并放到我的RV1126的/tm…...
Spring Boot 的自动装配
Spring Boot 的自动装配(Auto Configuration)是其核心特性之一,通过智能化的条件判断和配置加载机制,极大简化了传统 Spring 应用的配置复杂度。其原理和实现过程可概括为以下几个关键点: 一、核心触发机制:…...
Python中的“泛型”和“多重继承”
“泛型”和“多重继承”属于 Python 的语法规则。 1. 泛型(Generic[T]) 通俗解释 泛型允许你在定义类或函数时,不指定具体的类型,而是使用一个“占位符”(通常命名为 T)。这就像你制作一个盒子࿰…...
【C++】多参数构造函数使用explict的情形
在 C 中,“无非默认值” 指的是:构造函数的参数没有设置默认值(即所有参数都必须显式传递)。这个说法通常出现在讨论多参数构造函数是否需要使用 explicit 关键字时。 具体解释 多参数构造函数: • 如果一个构造函数有…...
C# Unity 唐老狮 No.10 模拟面试题
本文章不作任何商业用途 仅作学习与交流 安利唐老狮与其他老师合作的网站,内有大量免费资源和优质付费资源,我入门就是看唐老师的课程 打好坚实的基础非常非常重要: Unity课程 - 游习堂 - 唐老狮创立的游戏开发在线学习平台 - Powered By EduSoho C# 1. 内存中,堆和…...
使用DeepSeek,优化斐波那契数函数,效果相当不错
下面这段代码定义了一个递归函数 fibonacci,用于计算第 n 个斐波那契数。 def fibonacci(n):if n < 1:return nelse:return fibonacci(n - 1) fibonacci(n - 2)虽然代码逻辑正确,但其性能较差,尤其是对于较大的 n 值,其复杂度…...
【GPT入门】第22课 langchain LCEL介绍
【GPT入门】第22课 langchain LCEL介绍 1. LCEL介绍与特点2. 原生API与LCEL的对比2. 简单demo 1. LCEL介绍与特点 LCEL 即 LangChain Expression Language,是 LangChain 推出的一种声明式语言,用于简化和优化在 LangChain 框架内构建复杂链和应用的过程…...
市场监管总局升级12315平台 专项整治四大市场顽疾保障消费安全
大湾区经济网湾区财经讯,在今天下午举行的国务院新闻办新闻发布会上,市场监管总局负责人表示,将开展食品非法添加、假冒伪劣、价格欺诈、虚假宣传四大领域专项整治行动,并强化缺陷产品召回监管,全面保障消费者“安全消…...
FineBI6.x进阶篇-可视化专题
参考 参考:https://edu.fanruan.com/video/526 如何选择合适的图表:https://help.fanruan.com/dvg/doc-view-3.html 参考:https://help.fanruan.com/finebi/ 什么是GLAD原则:https://help.fanruan.com/dvg/doc-view-81.html …...
一对一交友App源码开发新趋势:精准匹配与多元盈利模式解析
一、 营商环境分析:机遇与挑战并存 近年来,随着移动互联网的普及和用户需求的不断升级,一对一交友App市场呈现出蓬勃发展的态势。然而,机遇与挑战并存,开发者需要审时度势,才能在激烈的市场竞争中脱颖而出…...
算法基础篇(蓝桥杯常考点)
算法基础篇 前言 算法内容还有搜索,数据结构(进阶),动态规划和图论 数学那个的话大家也知道比较难,放在最后讲 这期包含的内容可以看目录 模拟那个算法的话就是题说什么写什么,就不再分入目录中了 注意事…...
【Pandas】pandas Series cat
# Pandas2.2 Series ## Accessors |方法|描述| |-|:-------| |Series.str|对 Series 中的字符串元素进行矢量化字符串操作| |Series.cat|用于处理分类数据(Categorical Data)的属性| ### pandas.Series.cat pandas.Series.cat 是用于处理分类数据&am…...
Python的类和对象(3)
1、类的继承 父类:被继承 子类:继承者 – 可以使用父类的所有属性和方法、大大简化代码。 语法:在类名字后加上(继承的类名字) # 定义类 -- 父类 class Car:# 定义类属性wheel "有车轮"engine "有引…...
C语言修饰符汇总详解
在C语言中,修饰符(或称类型限定符)是用于修饰数据类型的关键字,可以改变变量或类型的性质和行为。它们通常用来控制存储类型、符号、大小、对齐、访问权限等。C语言中的修饰符主要可以分为以下几类: 类型修饰符存储类别修饰符常量和易变修饰符以下是C语言中所有修饰符的详…...
猎豹移动(Cheetah Mobile)
本文来自腾讯元宝 公司背景与发展历程 成立与早期定位 猎豹移动成立于2010年11月,由金山安全与可牛影像合并而成,初期以移动安全工具和清理软件为核心业务。其明星产品包括《猎豹清理大师》(Clean Master)和《猎豹浏览器》&…...
VLN 论文精读(二)VL-Nav: Real-time Vision-Language Navigation with Spatial Reasoning
这篇笔记用来描述2025年发表在arxiv上的一篇有关VLN领域的论文,由纽约州立大学布法罗分校和卡耐基梅隆联合发布。其主要创新点在于:像素级的视觉-语意特征、低算力移动设备部署、30Hz控制实时性、VLM模型的zero-shot ; 整个导航流程大概如下…...
优选算法系列(2.滑动窗口 _ 上)
目录 解法⼀(暴力求解)(不会超时,可以通过):一.长度最小的子数组(medium) 题目链接209. 长度最小的子数组 - 力扣(LeetCode) 解法: 代码&#…...
RK3568 android11 基于PN7160的NXP NFC移植
一,概述 1. 模块概述 PN7160专为在各种系统实现快速集成而设计,支持所有符合NFC Forum的模式,包括Android和Linux驱动程序,并支持实时操作系统和无操作系统的应用(PN7160不符合EMVCo)。嵌入式NFC固件减少了对主机交互的需求,并最大限度缩小了代码尺寸,使设计更轻松。…...
【网工第6版】第1章 计算机网络概论
目录 1计算机网络形成和发展 ■计算机网络 ■我国互联网发展 ■计算机网路分类 ■计算机网络应用 2 OSI和TCP/IP参考模型 ■网络分层的意义 ■OSI参考模型 ■TCP/IP参考模型 ■TCP/IP参考模型协议 3 数据封装与解封过程 ■封装 ■解封 1计算机网络形成和发展 ■计…...
【嵌入式】keil5安装(同时兼容C51和STM32)
最近在开发STM32的时候,安装Keil5,遇到STM32和C51的共存的问题,在网上找了很多方法,又遇到一些bug,最终还是弄好了。因此将处理的过程记录下来,希望对遇到相同问题的朋友一些启发。 1、下载安装包 Keil P…...
每日一题--进程与协程的区别
进程是什么? 进程(Process) 是操作系统进行 资源分配和调度的基本单位,代表一个正在执行的程序实例。每个进程拥有独立的虚拟地址空间、代码、数据和系统资源(如文件句柄、网络端口等)。进程之间通过 IPC&…...
网络安全运维应急响应与溯源分析实战案例
在日常运维过程中,网络安全事件时有发生,快速响应和精准溯源是保障业务稳定运行的关键。本文将通过一个实际案例,详细解析从发现问题到溯源定位,再到最终解决的完整流程。 目录 一、事件背景 二、事件发现 1. 监控告警触发 2…...
leetcode29. 两数相除-medium
1 题目:两数相除 官方标定难度:中 给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断,也就是截去(truncate)其小数部分。…...
深海300米的低温环境中的BMS优化方法
在深海300米的低温环境中,采用扩展卡尔曼滤波(EKF)结合温度补偿模型实现电池管理系统(BMS)的荷电状态(SOC)精确估计,需通过以下步骤实现: 1. 低温电池模型建立 1.1 电池…...
高主频GPU+RTX4090:AI生图性能优化超150%
概述:消费级高主频CPU搭配 RTX 4090显卡可以显著提高AI生图的性能,相比于企业级CPU具有更大的吞吐量和更优的成本效益。 引言:在AI图像生成过程中,CPU与GPU的协同效应对系统的整体性能至关重要。测试表明,与RTX 4090显…...
OpenCV 图像双线性插值
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 双线性插值是一种 二维插值方法,用于计算 栅格(Grid) 或 像素点 之间的插值值。它主要用于 图像缩放、旋转、变换 等操作,以在新像素位置估算灰度值或颜色值。 如上图所示,假设存在一个二维离散函数(如图像)…...
OpenCV计算摄影学(23)艺术化风格化处理函数stylization()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 风格化的目的是生成不以照片写实为目标的多种多样数字图像效果。边缘感知滤波器是风格化处理的理想选择,因为它们能够弱化低对比度区…...
《我的Python觉醒之路》之转型Python(十五)——控制流
[今天是2025年3月17日,继续复习第一章节、第二章节的内容 ] 《我的Python觉醒之路》之转型Python(十四)——控制流...
内存管理
一.什么是内存管理呢? 我们可以来看一下这个图,我们来了解一下这些地方都存的是什么。 数据段就是存放全局和静态变量的,代码段是存放常量的。 栈 局部变量:在函数内部定义的变量,其存储空间在栈上分配。当函数被调用时…...
学习threejs,使用MeshLambertMaterial漫反射材质
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.MeshLambertMaterial…...
P41-指针进阶1、2
1.字符指针 2.数组指针 3.指针数组 4.数组传参和指针传参 5.函数指针 6.函数指针数组 7.指向函数指针数组的指针 8.回调函数 9.指针和数组面试题的解析 指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念 1.指针就是个…...
旅游类小程序界面设计
产品概述 艾啦游是一款互联网旅游类小程序,致力于国内精品旅游,以及拥有自由行、专属热榜单、出行攻略等诸多功能,汇聚了许多国内的人气景点,与诸多城市的酒店也保持合作,打造一体式旅行服务,更有不断上新…...
探索具身多模态大模型:开发、数据集和未来方向(下)
25年2月来自广东人工智能和数字经济实验室、深圳大学、巴黎理工学院和巴黎高等师范学院、中山大学的论文“Exploring Embodied Multimodal Large Models: Development, Datasets, and Future Directions”。 近年来,具身多模态大模型 (EMLM) 因其在复杂的现实环境中…...
14 结构体
结构体 结构体是什么? 在前面我们学习过基础的数据类型int float char 等,都只能用来表示基础的数据类型,那么要怎么来表示复杂的数据类型呢? 比如学生信息: 学号姓名性别年龄总分数100maye男18666101椰汁女19555 …...
如何配置 Docker 以实现无需 sudo 使用
1. 背景知识:为什么需要 sudo? Docker 是一个容器化平台,其核心组件包括: Docker 守护进程(dockerd):负责管理容器的创建、运行和销毁。Docker CLI:用户通过命令行工具(…...
嵌入式开发之STM32学习笔记day06
基于STM32F103C8T6的开发实践——从入门到精通01 1. 引言 STM32系列微控制器是STMicroelectronics推出的一款高性能、低功耗的32位微控制器,广泛应用于嵌入式系统中。STM32F103C8T6是其中非常受欢迎的一款,凭借其强大的性能、丰富的外设接口和低廉的价格…...
openocd C#桌面工具
文章目录 简介一、主界面二、文件列表三、rtl介绍四、虚拟示波器1、画线2、画点3、合并显示4、测试代码简介 基于廉价9.9包邮的DAP-Link,在IAR和Keil中下载和调试都没毛病。 可是不能单独使用。单独烧录固件用不了,也不能同jlink一样打印日志和显示波形。 openocd开源工具能…...
goland小问题报错及解决
报错信息: cannot use rw (variable of type *populateResponse) as ResponseWriter value in argument to t.fh.ServeHTTP: *populateResponse does not implement ResponseWriter (missing method Fprintf) cannot use rw (variable of type *http2responseWrite…...
AtCoder Beginner Contest 397 A - D题解
Tasks - OMRON Corporation Programming Contest 2025 (AtCoder Beginner Contest 397) 本文为 AtCoder Beginner Contest 397 A - D题解 题目A: 代码(C): #include <bits/stdc.h>int main() {double n;std::cin >> n;if (n > 38.0) {std::cout << 1;}…...
18年老牌软件,完美解锁pro!
很多小伙伴在学习、工作中都喜欢使用思维导图来整理和记录自己的思路,通过图形化的方式展示复杂概念和关系,使信息更加清晰易懂,有助于理解和记忆;其次,分层次和分类别的信息结构也能够快速理清思路,突出重…...
Power Apps 技术分享:画布应用使用表单控件
前言 表单控件,是画布应用里一个非常好用的控件,我们今天简单介绍下,如何使用这个控件。 正文 1.首先,我们需要有一个数据源,我们这里用上一篇博客新建的数据源,如下图: 2.新建一个页面…...
视频转音频, 音频转文字
Ubuntu 24 环境准备 # 系统级依赖 sudo apt update && sudo apt install -y ffmpeg python3-venv git build-essential python3-dev# Python虚拟环境 python3 -m venv ~/ai_summary source ~/ai_summary/bin/activate核心工具链 工具用途安装命令Whisper语音识别pip …...
ZLMediaKit源码分析——[1] 开篇:onceToken源码分析
系列文章目录 第一篇 基于SRS 的 WebRTC 环境搭建 第二篇 基于SRS 实现RTSP接入与WebRTC播放 第三篇 centos下基于ZLMediaKit 的WebRTC 环境搭建 第四篇 WebRTC学习一:获取音频和视频设备 第五篇 WebRTC学习二:WebRTC音视频数据采集 第六篇 WebRTC学习三:WebRTC音视频约束 …...
EasyRTC嵌入式音视频通话SDK:微信生态支持、轻量化架构与跨平台兼容性(Linix/Windows/ARM/Android/iOS/LiteOS)
随着WebRTC技术的不断发展,实时音视频通信在各个领域的应用越来越广泛。EasyRTC嵌入式音视频通话SDK作为一款基于WebRTC技术的实时通信解决方案,凭借其强大的功能和灵活的集成能力,受到了越来越多开发者的关注。 一、系统架构设计 纯C语言开…...
Python中使用vlc库实现视频播放功能
文章目录 前言1. 环境准备1.1Python安装1.2选择Python开发环境1.3安装必要库 2. 基础播放示例3. 常用播放控制功能4. 事件监听5. 播放网络流媒体6. 结合 GUI 库制作视频播放器(以 Tkinter 为例) 前言 本教程主要包含打开文件、播放和停止按钮࿰…...
Elasticsearch面试题
以下是150道Elasticsearch面试题及其详细回答,涵盖了Elasticsearch的基础知识、配置与优化、查询操作、集群管理、性能调优等多个方面,每道题目都尽量详细且简单易懂: Elasticsearch基础概念类 1. 什么是Elasticsearch? Elastics…...
Linux上位机开发实战(qt编译之谜)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 很多同学都喜欢用IDE,也能理解。因为不管是visual studio qt插件,还是qt creator其实都帮我们做了很多额外的工作。这里面最…...
SpringBoot3+Druid+MybatisPlus多数据源支持,通过@DS注解配置Service/Mapper/Entity使用什么数据源
在 Spring Boot 3 中结合 Druid 和 MyBatis-Plus 实现多数据源支持,并通过 DS 注解动态切换数据源,可以按照以下步骤进行配置: 官方文档:多数据源支持 | MyBatis-Plus 1. 引入依赖 在 pom.xml 中添加以下依赖: <…...