Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 659de06

Browse files
committed
feat: add social network adapter
1 parent 1fe57a3 commit 659de06

File tree

4 files changed

+226
-0
lines changed

4 files changed

+226
-0
lines changed

contracts/TestContracts.sol

+13
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,16 @@ contract TestDeposit {
237237
emit TestExecute(depositor, num, addresses[1], message);
238238
}
239239
}
240+
241+
contract SocialNetworkControllerMock {
242+
uint256 public constant HEART_BTC = 369;
243+
uint256 public bitcoinStaked = 0;
244+
245+
event Stake(address indexed user, uint256 amount);
246+
247+
function stakeBTC(uint256 amount, address recipient) external {
248+
uint256 mintAmount = amount * HEART_BTC;
249+
bitcoinStaked += amount;
250+
emit Stake(recipient, mintAmount);
251+
}
252+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// The Licensed Work is (c) 2022 Sygma
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
pragma solidity 0.8.11;
4+
5+
import "../interfaces/IBridge.sol";
6+
import "../interfaces/IERCHandler.sol";
7+
import "../interfaces/ISocialNetwork.sol";
8+
import "../handlers/fee/PercentageERC20FeeHandlerEVM.sol";
9+
import "../ERC20Safe.sol";
10+
11+
12+
contract SocialNetworkAdapter is PercentageERC20FeeHandlerEVM {
13+
14+
address public immutable _permissionlessHandler;
15+
ISocialNetwork public immutable _socialNetworkController;
16+
17+
mapping(string => mapping(address => uint256)) public _btcToEthDepositorToAmount;
18+
19+
20+
function _onlyPermissionlessHandler() private view {
21+
require(msg.sender == _permissionlessHandler, "sender must be bridge contract");
22+
}
23+
24+
modifier onlyPermissionlessHandler() {
25+
_onlyPermissionlessHandler();
26+
_;
27+
}
28+
29+
constructor (
30+
address bridgeAddress,
31+
address permissionlessHandler,
32+
ISocialNetwork socialNetworkController,
33+
address feeHandlerRouterAddress
34+
) PercentageERC20FeeHandlerEVM(bridgeAddress, feeHandlerRouterAddress) {
35+
_permissionlessHandler = permissionlessHandler;
36+
_socialNetworkController = socialNetworkController;
37+
}
38+
39+
event TestExecute(address depositor, uint256 depositAmount, string btcDepositorAddress);
40+
41+
function stakeBTC (address ethDepositorAddress, bytes calldata data) external onlyPermissionlessHandler {
42+
(uint256 amount, string memory btcDepositorAddress) = abi.decode(data, (uint256, string));
43+
44+
_socialNetworkController.stakeBTC(amount, ethDepositorAddress);
45+
_btcToEthDepositorToAmount[btcDepositorAddress][ethDepositorAddress] = amount;
46+
}
47+
48+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// The Licensed Work is (c) 2022 Sygma
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
pragma solidity 0.8.11;
4+
5+
/**
6+
@title Interface for SocialNetwork adapter.
7+
@author ChainSafe Systems.
8+
*/
9+
interface ISocialNetwork {
10+
function stakeBTC (uint256 amount, address ethDepositorAddress) external;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// The Licensed Work is (c) 2022 Sygma
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
const TruffleAssert = require("truffle-assertions");
5+
const Ethers = require("ethers");
6+
const Helpers = require("../../helpers");
7+
8+
const PermissionlessGenericHandlerContract = artifacts.require(
9+
"PermissionlessGenericHandler"
10+
);
11+
const SocialAdapterContract = artifacts.require("SocialNetworkAdapter");
12+
const SocialNetworkControllerMockContract = artifacts.require("SocialNetworkControllerMock");
13+
14+
contract(
15+
"PermissionlessGenericHandler - Social network - [Execute Proposal]",
16+
async (accounts) => {
17+
const originDomainID = 1;
18+
const destinationDomainID = 2;
19+
const expectedDepositNonce = 1;
20+
21+
const ethDepositorAddress = accounts[1];
22+
const relayer1Address = accounts[2];
23+
24+
const feeData = "0x";
25+
const destinationMaxFee = 900000;
26+
27+
28+
let BridgeInstance;
29+
let SocialNetworkAdapterInstance;
30+
let SocialNetworkControllerMockInstance;
31+
32+
let resourceID;
33+
let depositFunctionSignature;
34+
let PermissionlessGenericHandlerInstance;
35+
36+
beforeEach(async () => {
37+
await Promise.all([
38+
(BridgeInstance = await Helpers.deployBridge(
39+
destinationDomainID,
40+
accounts[0]
41+
)),
42+
]);
43+
44+
resourceID = "0x0000000000000000000000000000000000000000000000000000000000000000"
45+
46+
PermissionlessGenericHandlerInstance =
47+
await PermissionlessGenericHandlerContract.new(BridgeInstance.address);
48+
49+
SocialNetworkControllerMockInstance = await SocialNetworkControllerMockContract.new();
50+
SocialNetworkAdapterInstance = await SocialAdapterContract.new(
51+
BridgeInstance.address,
52+
PermissionlessGenericHandlerInstance.address,
53+
SocialNetworkControllerMockInstance.address,
54+
Ethers.constants.AddressZero
55+
)
56+
57+
depositFunctionSignature = Helpers.getFunctionSignature(
58+
SocialNetworkAdapterInstance,
59+
"stakeBTC"
60+
);
61+
62+
const PermissionlessGenericHandlerSetResourceData =
63+
Helpers.constructGenericHandlerSetResourceData(
64+
depositFunctionSignature,
65+
Helpers.blankFunctionDepositorOffset,
66+
Helpers.blankFunctionSig
67+
);
68+
await BridgeInstance.adminSetResource(
69+
PermissionlessGenericHandlerInstance.address,
70+
resourceID,
71+
SocialNetworkAdapterInstance.address,
72+
PermissionlessGenericHandlerSetResourceData
73+
);
74+
75+
// set MPC address to unpause the Bridge
76+
await BridgeInstance.endKeygen(Helpers.mpcAddress);
77+
});
78+
79+
it("call with packed depositData should be successful", async () => {
80+
const depositAmount = 5;
81+
const btcDepositorAddress = "btcDepositorAddress"
82+
const executionData = Helpers.abiEncode(["uint", "string"], [depositAmount, btcDepositorAddress]);
83+
84+
// this mocks prepareDepositData helper function from origin adapter
85+
// this logic is now on implemented on relayers
86+
const preparedExecutionData =
87+
"0x" +
88+
Helpers.abiEncode(
89+
["address", "bytes"], [Ethers.constants.AddressZero, executionData]
90+
).slice(66);
91+
92+
console.log("preparedExecutionData", preparedExecutionData);
93+
const depositFunctionSignature = Helpers.getFunctionSignature(
94+
SocialNetworkAdapterInstance,
95+
"stakeBTC"
96+
);
97+
const depositData = Helpers.createPermissionlessGenericDepositData(
98+
depositFunctionSignature,
99+
SocialNetworkAdapterInstance.address,
100+
destinationMaxFee,
101+
ethDepositorAddress,
102+
preparedExecutionData
103+
);
104+
105+
const proposal = {
106+
originDomainID: originDomainID,
107+
depositNonce: expectedDepositNonce,
108+
data: depositData,
109+
resourceID: resourceID,
110+
};
111+
const proposalSignedData = await Helpers.signTypedProposal(
112+
BridgeInstance.address,
113+
[proposal]
114+
);
115+
116+
// relayer1 executes the proposal
117+
const executeTx = await BridgeInstance.executeProposal(proposal, proposalSignedData, {
118+
from: relayer1Address,
119+
});
120+
121+
const internalTx = await TruffleAssert.createTransactionResult(
122+
SocialNetworkControllerMockInstance,
123+
executeTx.tx
124+
);
125+
126+
// check that ProposalExecution event is emitted
127+
TruffleAssert.eventEmitted(executeTx, "ProposalExecution", (event) => {
128+
return (
129+
event.originDomainID.toNumber() === originDomainID &&
130+
event.depositNonce.toNumber() === expectedDepositNonce
131+
);
132+
});
133+
134+
// check that TestExecute event is emitted
135+
TruffleAssert.eventEmitted(internalTx, "Stake", (event) => {
136+
return (
137+
event.user === ethDepositorAddress &&
138+
// this is for Social network internal logic
139+
// 36900 Social Network Bitcoin (HEART) for every Bitcoin (SAT) deposited
140+
event.amount.toNumber() === depositAmount * 369
141+
);
142+
});
143+
144+
// check that amount is mapped to belonging address
145+
assert.equal(
146+
await SocialNetworkAdapterInstance._btcToEthDepositorToAmount.call(
147+
btcDepositorAddress,
148+
ethDepositorAddress
149+
),
150+
depositAmount
151+
)
152+
});
153+
}
154+
);

0 commit comments

Comments
 (0)