Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/XanV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ contract XanV1 is
}

/// @notice Initializes the XanV1 contract.
/// @param initialMintRecipient The initial recipient of the minted tokens.
/// @param initialMintRecipient The distributor address being the initial recipient of the minted tokens and
/// authorized caller of the `transferAndLock` function.
/// @param council The address of the governance council contract.
function initializeV1( /* solhint-disable-line comprehensive-interface*/
address initialMintRecipient,
Expand All @@ -100,6 +101,7 @@ contract XanV1 is

// Initialize the XanV1 contract
_mint(initialMintRecipient, Parameters.SUPPLY);
_getLockingData().transferAndLockCaller = initialMintRecipient;
_getCouncilData().council = council;
}

Expand All @@ -110,6 +112,9 @@ contract XanV1 is

/// @inheritdoc IXanV1
function transferAndLock(address to, uint256 value) external override {
if (_getLockingData().transferAndLockCaller != msg.sender) {
revert UnauthorizedCaller({caller: msg.sender});
}
_transfer({from: msg.sender, to: to, value: value});
_lock({account: to, value: value});
}
Expand Down
3 changes: 2 additions & 1 deletion src/drafts/XanV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ contract XanV2 is IXanV2, XanV1 {
}

/// @notice Initializes the XanV2 contract.
/// @param initialMintRecipient The initial recipient of the minted tokens.
/// @param initialMintRecipient The distributor address being the initial recipient of the minted tokens and
/// authorized caller of the `transferAndLock` function.
/// @param council The address of the governance council contract.
/// @param xanV2Forwarder The XanV2 forwarder contract.
/// @custom:oz-upgrades-validate-as-initializer
Expand Down
3 changes: 2 additions & 1 deletion src/interfaces/IXanV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ interface IXanV1 {
/// @param value The value to lock.
function lock(uint256 value) external;

/// @notice Transfers tokens and immediately locks them.
/// @notice Transfers tokens and immediately locks them. This function is only callable by the authorized token
/// distributor address.
/// @param to The receiver.
/// @param value The value to be transferred and locked.
function transferAndLock(address to, uint256 value) external;
Expand Down
2 changes: 2 additions & 0 deletions src/libs/Locking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ library Locking {
/// @notice A struct containing data associated with the token-locking mechanism.
/// @param lockedBalances The locked balances associated with the current implementation.
/// @param lockedSupply The locked total supply associated with the current implementation.
/// @param transferAndLockCaller The address being authorized to call the `transferAndLock` function.
struct Data {
mapping(address owner => uint256) lockedBalances;
uint256 lockedSupply;
address transferAndLockCaller;
}
}
17 changes: 17 additions & 0 deletions test/XanV1.locking.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,23 @@ contract XanV1LockingTest is Test {
_xanProxy.transferAndLock({to: _RECEIVER, value: 1});
}

function test_transferAndLock_reverts_if_the_caller_is_not_authorized(address other) public {
vm.assume(other != _defaultSender && other != address(0));

// Fund an address that is not the authorized `transferAndLock` caller.
uint256 value = 100;
{
vm.prank(_defaultSender);
_xanProxy.transfer(other, value);
assertEq(_xanProxy.unlockedBalanceOf(other), 100);
}

// Attempt to call `transferAndLock`
vm.prank(other);
vm.expectRevert(abi.encodeWithSelector(XanV1.UnauthorizedCaller.selector, other), address(_xanProxy));
_xanProxy.transferAndLock({to: _RECEIVER, value: value});
}

function test_transfer_transfers_and_locks_tokens() public {
uint256 supply = _xanProxy.totalSupply();

Expand Down