Skip to content

Commit

Permalink
Merge branch 'master' into feat/simple-dao-template
Browse files Browse the repository at this point in the history
  • Loading branch information
zhifenglee-aelf committed Jun 25, 2024
2 parents cc473cc + 36894b8 commit ae19cb0
Show file tree
Hide file tree
Showing 22 changed files with 3,731 additions and 0 deletions.
60 changes: 60 additions & 0 deletions .github/workflows/publish-templates-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
on:
push:
tags:
- 'templates-v*'

jobs:
publish:
runs-on: ubuntu-latest
env:
WORKING_DIRECTORY: templates
environment: prod
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0 # Ensure the full history is fetched so we can check the commit history

- name: Verify tag is on master branch
id: verify_tag
run: |
# Get the commit SHA of the tag
TAG_COMMIT=$(git rev-list -n 1 $GITHUB_REF)
# Check if the commit exists on the master branch
if git merge-base --is-ancestor $TAG_COMMIT origin/master; then
echo "Tag commit is on master branch."
echo "IS_ON_MASTER=true" >> $GITHUB_ENV
else
echo "Tag commit is not on master branch."
echo "IS_ON_MASTER=false" >> $GITHUB_ENV
fi
- name: Stop if not on master
if: env.IS_ON_MASTER != 'true'
run: |
echo "This tag was not created from the master branch. Exiting."
exit 1
- name: Setup .NET
if: env.IS_ON_MASTER == 'true'
uses: actions/setup-dotnet@v1
with:
dotnet-version: '7.0.*' # Change this to the .NET version you're using

- name: Read version from Version.props
if: env.IS_ON_MASTER == 'true'
working-directory: ${{ env.WORKING_DIRECTORY }}
run: |
version=$(grep -oP '<Version>\K[^<]+' Version.props)
echo "VERSION=$version" >> $GITHUB_ENV
- name: Pack
if: env.IS_ON_MASTER == 'true'
working-directory: ${{ env.WORKING_DIRECTORY }}
run: dotnet pack --configuration Release --output nupkgs /p:Version=$VERSION

- name: Publish NuGet packages
if: env.IS_ON_MASTER == 'true'
working-directory: ${{ env.WORKING_DIRECTORY }}
run: |
dotnet nuget push "nupkgs/*.nupkg" --api-key ${{ secrets.TEMPLATES_NUGET_API_KEY }} --source ${{ vars.TEMPLATES_NUGET_SOURCE_URL }}
34 changes: 34 additions & 0 deletions .github/workflows/publish-templates-staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
on:
push:
branches:
- release/templates/*

jobs:
publish:
runs-on: ubuntu-latest
env:
WORKING_DIRECTORY: templates
environment: staging
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: '7.0.*' # Change this to the .NET version you're using

- name: Read version from Version.props
working-directory: ${{ env.WORKING_DIRECTORY }}
run: |
version=$(grep -oP '<Version>\K[^<]+' Version.props)
echo "VERSION=$version-rc.${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: Pack
working-directory: ${{ env.WORKING_DIRECTORY }}
run: dotnet pack --configuration Release --output nupkgs /p:Version=$VERSION

- name: Publish NuGet packages
working-directory: ${{ env.WORKING_DIRECTORY }}
run: |
dotnet nuget push "nupkgs/*.nupkg" --api-key ${{ secrets.TEST_TEMPLATES_NUGET_API_KEY }} --source ${{ vars.TEST_TEMPLATES_NUGET_SOURCE_URL }}
6 changes: 6 additions & 0 deletions templates/AElf.Contract.Template.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@
<IncludeContentInPack>true</IncludeContentInPack>
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>content</ContentTargetFolders>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>

<ItemGroup>
<Content Include="HelloWorldContract\**\*" Exclude="HelloWorldContract\**\bin\**;HelloWorldContract\**\obj\**" />
<Content Include="LotteryGameContract\**\*" Exclude="LotteryGameContract\**\bin\**;LotteryGameContract\**\obj\**" />
<Content Include="SimpleDAOContract\**\*" Exclude="SimpleDAOContract\**\bin\**;SimpleDAOContract\**\obj\**" />
<Compile Remove="**\*" />
</ItemGroup>

<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="AElf.Cryptography" Version="1.5.0" />
</ItemGroup>
Expand Down
21 changes: 21 additions & 0 deletions templates/LotteryGameContract/.template.config/template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "http://json.schemastore.org/template",
"author": "AElf",
"classifications": [
"AElf/SmartContract"
],
"identity": "AElf.Contract.LotteryGame.Template",
"name": "AElf Contract LotteryGame Template",
"shortName": "aelf-lottery",
"tags": {
"language": "C#",
"type": "project"
},
"sourceName": "LotteryGame",
"symbols": {
"NamespacePath": {
"type": "parameter",
"replaces": "AElf.Contracts.LotteryGame"
}
}
}
11 changes: 11 additions & 0 deletions templates/LotteryGameContract/src/ContractReferences.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using AElf.Contracts.Consensus.AEDPoS;
using AElf.Contracts.MultiToken;

namespace AElf.Contracts.LotteryGame
{
public partial class LotteryGameState
{
internal TokenContractContainer.TokenContractReferenceState TokenContract { get; set; }
internal AEDPoSContractContainer.AEDPoSContractReferenceState ConsensusContract { get; set; }
}
}
227 changes: 227 additions & 0 deletions templates/LotteryGameContract/src/LotteryGame.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
using AElf.Contracts.MultiToken;
using AElf.Sdk.CSharp;
using AElf.Types;
using Google.Protobuf.WellKnownTypes;

namespace AElf.Contracts.LotteryGame
{
// Contract class must inherit the base class generated from the proto file
public class LotteryGame : LotteryGameContainer.LotteryGameBase
{
private const string TokenContractAddress = "ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx"; // tDVW token contract address
private const string TokenSymbol = "ELF";
private const long MinimumPlayAmount = 1_000_000; // 0.01 ELF
private const long MaximumPlayAmount = 1_000_000_000; // 10 ELF

// Initializes the contract
public override Empty Initialize(Empty input)
{
// Check if the contract is already initialized
Assert(State.Initialized.Value == false, "Already initialized.");
// Set the contract state
State.Initialized.Value = true;
// Set the owner address
State.Owner.Value = Context.Sender;

// Initialize the token contract
State.TokenContract.Value = Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName);
// The below code can be used to replace the above line. The below is a showcase of how you can reference to any contracts.
// State.TokenContract.Value = Address.FromBase58(TokenContractAddress);
State.ConsensusContract.Value = Context.GetContractAddressByName(SmartContractConstants.ConsensusContractSystemName);

return new Empty();
}

// Plays the lottery game with a specified amount of tokens.
// The method checks if the play amount is within the limit.
// If the player wins, tokens are transferred from the contract to the sender and a PlayOutcomeEvent is fired with the won amount.
// If the player loses, tokens are transferred from the sender to the contract and a PlayOutcomeEvent is fired with the lost amount.
public override Empty Play(Int64Value input)
{
var playAmount = input.Value;

// Check if input amount is within the limit
Assert(playAmount is >= MinimumPlayAmount and <= MaximumPlayAmount, "Invalid play amount.");

// Check if the sender has enough tokens
var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
{
Owner = Context.Sender,
Symbol = TokenSymbol
}).Balance;
Assert(balance >= playAmount, "Insufficient balance.");

// Check if the contract has enough tokens
var contractBalance = State.TokenContract.GetBalance.Call(new GetBalanceInput
{
Owner = Context.Self,
Symbol = TokenSymbol
}).Balance;
Assert(contractBalance >= playAmount, "Insufficient contract balance.");

// Get a random hash and check if it is available
var randomHash = State.ConsensusContract.GetRandomHash.Call(new Int64Value
{
Value = Context.CurrentHeight
});
Assert(randomHash != null && !randomHash.Value.IsNullOrEmpty(), "Still preparing your game result, please wait for a while...");

if(IsWinner(randomHash))
{
// Transfer the token from the contract to the sender
State.TokenContract.Transfer.Send(new TransferInput
{
To = Context.Sender,
Symbol = TokenSymbol,
Amount = playAmount
});

// Emit an event to notify listeners about the outcome
Context.Fire(new PlayOutcomeEvent
{
Amount = input.Value,
Won = playAmount
});
}
else
{
// Transfer the token from the sender to the contract
State.TokenContract.TransferFrom.Send(new TransferFromInput
{
From = Context.Sender,
To = Context.Self,
Symbol = TokenSymbol,
Amount = playAmount
});

// Emit an event to notify listeners about the outcome
Context.Fire(new PlayOutcomeEvent
{
Amount = input.Value,
Won = -playAmount
});
}

return new Empty();
}

// Withdraws a specified amount of tokens from the contract.
// This method can only be called by the owner of the contract.
// After the tokens are transferred, a WithdrawEvent is fired to notify any listeners about the withdrawal.
public override Empty Withdraw(Int64Value input)
{
AssertIsOwner();

// Transfer the token from the contract to the sender
State.TokenContract.Transfer.Send(new TransferInput
{
To = Context.Sender,
Symbol = TokenSymbol,
Amount = input.Value
});

// Emit an event to notify listeners about the withdrawal
Context.Fire(new WithdrawEvent
{
Amount = input.Value,
From = Context.Self,
To = State.Owner.Value
});

return new Empty();
}

// Deposits a specified amount of tokens into the contract.
// This method can only be called by the owner of the contract.
// After the tokens are transferred, a DepositEvent is fired to notify any listeners about the deposit.
public override Empty Deposit(Int64Value input)
{
AssertIsOwner();

// Transfer the token from the sender to the contract
State.TokenContract.TransferFrom.Send(new TransferFromInput
{
From = Context.Sender,
To = Context.Self,
Symbol = TokenSymbol,
Amount = input.Value
});

// Emit an event to notify listeners about the deposit
Context.Fire(new DepositEvent
{
Amount = input.Value,
From = Context.Sender,
To = Context.Self
});

return new Empty();
}

// Transfers the ownership of the contract to a new owner.
// This method can only be called by the current owner of the contract.
public override Empty TransferOwnership(Address input)
{
AssertIsOwner();

// Set the new owner address
State.Owner.Value = input;

return new Empty();
}

// A method that read the contract's play amount limit
public override PlayAmountLimitMessage GetPlayAmountLimit(Empty input)
{
// Wrap the value in the return type
return new PlayAmountLimitMessage
{
MinimumAmount = MinimumPlayAmount,
MaximumAmount = MaximumPlayAmount
};
}

// A method that read the contract's current balance
public override Int64Value GetContractBalance(Empty input)
{
// Get the balance of the contract
var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
{
Owner = Context.Self,
Symbol = TokenSymbol
}).Balance;

// Wrap the value in the return type
return new Int64Value
{
Value = balance
};
}

// A method that read the contract's owner
public override StringValue GetOwner(Empty input)
{
return State.Owner.Value == null ? new StringValue() : new StringValue {Value = State.Owner.Value.ToBase58()};
}

// Determines if the player is a winner.
// This method generates a random number based on the random hash input and checks if it's equal to 0.
// If the random number is 0, the player is considered a winner.
private bool IsWinner(Hash randomHash)
{
// Improve random distribution by XORing with the origin transaction ID
var randomHex = HashHelper.XorAndCompute(randomHash, Context.OriginTransactionId).ToHex();
var randomInt = int.Parse(randomHex.Substring(0, 8), System.Globalization.NumberStyles.HexNumber);
var result = randomInt % 2;
return result == 0;
}

// This method is used to ensure that only the owner of the contract can perform certain actions.
// If the context sender is not the owner, an exception is thrown with the message "Unauthorized to perform the action."
private void AssertIsOwner()
{
Assert(Context.Sender == State.Owner.Value, "Unauthorized to perform the action.");
}
}

}
Loading

0 comments on commit ae19cb0

Please sign in to comment.